Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -0,0 +1,584 @@
# SPDX-FileCopyrightText: 2006 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
set(SRC
FRS_freestyle.h
intern/application/AppCanvas.cpp
intern/application/AppCanvas.h
intern/application/AppConfig.cpp
intern/application/AppConfig.h
intern/application/AppView.cpp
intern/application/AppView.h
intern/application/Controller.cpp
intern/application/Controller.h
intern/blender_interface/BlenderFileLoader.cpp
intern/blender_interface/BlenderFileLoader.h
intern/blender_interface/BlenderStrokeRenderer.cpp
intern/blender_interface/BlenderStrokeRenderer.h
intern/blender_interface/BlenderStyleModule.h
intern/blender_interface/FRS_freestyle.cpp
intern/geometry/BBox.h
intern/geometry/Bezier.cpp
intern/geometry/Bezier.h
intern/geometry/FastGrid.cpp
intern/geometry/FastGrid.h
intern/geometry/FitCurve.cpp
intern/geometry/FitCurve.h
intern/geometry/Geom.h
intern/geometry/GeomCleaner.cpp
intern/geometry/GeomCleaner.h
intern/geometry/GeomUtils.cpp
intern/geometry/GeomUtils.h
intern/geometry/Grid.cpp
intern/geometry/Grid.h
intern/geometry/GridHelpers.cpp
intern/geometry/GridHelpers.h
intern/geometry/HashGrid.cpp
intern/geometry/HashGrid.h
intern/geometry/Noise.cpp
intern/geometry/Noise.h
intern/geometry/Polygon.h
intern/geometry/SweepLine.h
intern/geometry/VecMat.h
intern/geometry/matrix_util.cpp
intern/geometry/matrix_util.h
intern/geometry/normal_cycle.cpp
intern/geometry/normal_cycle.h
intern/image/GaussianFilter.cpp
intern/image/GaussianFilter.h
intern/image/Image.h
intern/image/ImagePyramid.cpp
intern/image/ImagePyramid.h
intern/python/BPy_BBox.cpp
intern/python/BPy_BBox.h
intern/python/BPy_BinaryPredicate0D.cpp
intern/python/BPy_BinaryPredicate0D.h
intern/python/BPy_BinaryPredicate1D.cpp
intern/python/BPy_BinaryPredicate1D.h
intern/python/BPy_ContextFunctions.cpp
intern/python/BPy_ContextFunctions.h
intern/python/BPy_Convert.cpp
intern/python/BPy_Convert.h
intern/python/BPy_Freestyle.cpp
intern/python/BPy_Freestyle.h
intern/python/BPy_FrsMaterial.cpp
intern/python/BPy_FrsMaterial.h
intern/python/BPy_FrsNoise.cpp
intern/python/BPy_FrsNoise.h
intern/python/BPy_Id.cpp
intern/python/BPy_Id.h
intern/python/BPy_IntegrationType.cpp
intern/python/BPy_IntegrationType.h
intern/python/BPy_Interface0D.cpp
intern/python/BPy_Interface0D.h
intern/python/BPy_Interface1D.cpp
intern/python/BPy_Interface1D.h
intern/python/BPy_Iterator.cpp
intern/python/BPy_Iterator.h
intern/python/BPy_MediumType.cpp
intern/python/BPy_MediumType.h
intern/python/BPy_Nature.cpp
intern/python/BPy_Nature.h
intern/python/BPy_Operators.cpp
intern/python/BPy_Operators.h
intern/python/BPy_SShape.cpp
intern/python/BPy_SShape.h
intern/python/BPy_StrokeAttribute.cpp
intern/python/BPy_StrokeAttribute.h
intern/python/BPy_StrokeShader.cpp
intern/python/BPy_StrokeShader.h
intern/python/BPy_UnaryFunction0D.cpp
intern/python/BPy_UnaryFunction0D.h
intern/python/BPy_UnaryFunction1D.cpp
intern/python/BPy_UnaryFunction1D.h
intern/python/BPy_UnaryPredicate0D.cpp
intern/python/BPy_UnaryPredicate0D.h
intern/python/BPy_UnaryPredicate1D.cpp
intern/python/BPy_UnaryPredicate1D.h
intern/python/BPy_ViewMap.cpp
intern/python/BPy_ViewMap.h
intern/python/BPy_ViewShape.cpp
intern/python/BPy_ViewShape.h
intern/python/BinaryPredicate1D/BPy_FalseBP1D.cpp
intern/python/BinaryPredicate1D/BPy_FalseBP1D.h
intern/python/BinaryPredicate1D/BPy_Length2DBP1D.cpp
intern/python/BinaryPredicate1D/BPy_Length2DBP1D.h
intern/python/BinaryPredicate1D/BPy_SameShapeIdBP1D.cpp
intern/python/BinaryPredicate1D/BPy_SameShapeIdBP1D.h
intern/python/BinaryPredicate1D/BPy_TrueBP1D.cpp
intern/python/BinaryPredicate1D/BPy_TrueBP1D.h
intern/python/BinaryPredicate1D/BPy_ViewMapGradientNormBP1D.cpp
intern/python/BinaryPredicate1D/BPy_ViewMapGradientNormBP1D.h
intern/python/Director.cpp
intern/python/Director.h
intern/python/Interface0D/BPy_CurvePoint.cpp
intern/python/Interface0D/BPy_CurvePoint.h
intern/python/Interface0D/BPy_SVertex.cpp
intern/python/Interface0D/BPy_SVertex.h
intern/python/Interface0D/BPy_ViewVertex.cpp
intern/python/Interface0D/BPy_ViewVertex.h
intern/python/Interface0D/CurvePoint/BPy_StrokeVertex.cpp
intern/python/Interface0D/CurvePoint/BPy_StrokeVertex.h
intern/python/Interface0D/ViewVertex/BPy_NonTVertex.cpp
intern/python/Interface0D/ViewVertex/BPy_NonTVertex.h
intern/python/Interface0D/ViewVertex/BPy_TVertex.cpp
intern/python/Interface0D/ViewVertex/BPy_TVertex.h
intern/python/Interface1D/BPy_FEdge.cpp
intern/python/Interface1D/BPy_FEdge.h
intern/python/Interface1D/BPy_FrsCurve.cpp
intern/python/Interface1D/BPy_FrsCurve.h
intern/python/Interface1D/BPy_Stroke.cpp
intern/python/Interface1D/BPy_Stroke.h
intern/python/Interface1D/BPy_ViewEdge.cpp
intern/python/Interface1D/BPy_ViewEdge.h
intern/python/Interface1D/Curve/BPy_Chain.cpp
intern/python/Interface1D/Curve/BPy_Chain.h
intern/python/Interface1D/FEdge/BPy_FEdgeSharp.cpp
intern/python/Interface1D/FEdge/BPy_FEdgeSharp.h
intern/python/Interface1D/FEdge/BPy_FEdgeSmooth.cpp
intern/python/Interface1D/FEdge/BPy_FEdgeSmooth.h
intern/python/Iterator/BPy_AdjacencyIterator.cpp
intern/python/Iterator/BPy_AdjacencyIterator.h
intern/python/Iterator/BPy_ChainPredicateIterator.cpp
intern/python/Iterator/BPy_ChainPredicateIterator.h
intern/python/Iterator/BPy_ChainSilhouetteIterator.cpp
intern/python/Iterator/BPy_ChainSilhouetteIterator.h
intern/python/Iterator/BPy_ChainingIterator.cpp
intern/python/Iterator/BPy_ChainingIterator.h
intern/python/Iterator/BPy_CurvePointIterator.cpp
intern/python/Iterator/BPy_CurvePointIterator.h
intern/python/Iterator/BPy_Interface0DIterator.cpp
intern/python/Iterator/BPy_Interface0DIterator.h
intern/python/Iterator/BPy_SVertexIterator.cpp
intern/python/Iterator/BPy_SVertexIterator.h
intern/python/Iterator/BPy_StrokeVertexIterator.cpp
intern/python/Iterator/BPy_StrokeVertexIterator.h
intern/python/Iterator/BPy_ViewEdgeIterator.cpp
intern/python/Iterator/BPy_ViewEdgeIterator.h
intern/python/Iterator/BPy_orientedViewEdgeIterator.cpp
intern/python/Iterator/BPy_orientedViewEdgeIterator.h
intern/python/StrokeShader/BPy_BackboneStretcherShader.cpp
intern/python/StrokeShader/BPy_BackboneStretcherShader.h
intern/python/StrokeShader/BPy_BezierCurveShader.cpp
intern/python/StrokeShader/BPy_BezierCurveShader.h
intern/python/StrokeShader/BPy_BlenderTextureShader.cpp
intern/python/StrokeShader/BPy_BlenderTextureShader.h
intern/python/StrokeShader/BPy_CalligraphicShader.cpp
intern/python/StrokeShader/BPy_CalligraphicShader.h
intern/python/StrokeShader/BPy_ColorNoiseShader.cpp
intern/python/StrokeShader/BPy_ColorNoiseShader.h
intern/python/StrokeShader/BPy_ConstantColorShader.cpp
intern/python/StrokeShader/BPy_ConstantColorShader.h
intern/python/StrokeShader/BPy_ConstantThicknessShader.cpp
intern/python/StrokeShader/BPy_ConstantThicknessShader.h
intern/python/StrokeShader/BPy_ConstrainedIncreasingThicknessShader.cpp
intern/python/StrokeShader/BPy_ConstrainedIncreasingThicknessShader.h
intern/python/StrokeShader/BPy_GuidingLinesShader.cpp
intern/python/StrokeShader/BPy_GuidingLinesShader.h
intern/python/StrokeShader/BPy_IncreasingColorShader.cpp
intern/python/StrokeShader/BPy_IncreasingColorShader.h
intern/python/StrokeShader/BPy_IncreasingThicknessShader.cpp
intern/python/StrokeShader/BPy_IncreasingThicknessShader.h
intern/python/StrokeShader/BPy_PolygonalizationShader.cpp
intern/python/StrokeShader/BPy_PolygonalizationShader.h
intern/python/StrokeShader/BPy_SamplingShader.cpp
intern/python/StrokeShader/BPy_SamplingShader.h
intern/python/StrokeShader/BPy_SmoothingShader.cpp
intern/python/StrokeShader/BPy_SmoothingShader.h
intern/python/StrokeShader/BPy_SpatialNoiseShader.cpp
intern/python/StrokeShader/BPy_SpatialNoiseShader.h
intern/python/StrokeShader/BPy_StrokeTextureStepShader.cpp
intern/python/StrokeShader/BPy_StrokeTextureStepShader.h
intern/python/StrokeShader/BPy_ThicknessNoiseShader.cpp
intern/python/StrokeShader/BPy_ThicknessNoiseShader.h
intern/python/StrokeShader/BPy_TipRemoverShader.cpp
intern/python/StrokeShader/BPy_TipRemoverShader.h
intern/python/UnaryFunction0D/BPy_UnaryFunction0DDouble.cpp
intern/python/UnaryFunction0D/BPy_UnaryFunction0DDouble.h
intern/python/UnaryFunction0D/BPy_UnaryFunction0DEdgeNature.cpp
intern/python/UnaryFunction0D/BPy_UnaryFunction0DEdgeNature.h
intern/python/UnaryFunction0D/BPy_UnaryFunction0DFloat.cpp
intern/python/UnaryFunction0D/BPy_UnaryFunction0DFloat.h
intern/python/UnaryFunction0D/BPy_UnaryFunction0DId.cpp
intern/python/UnaryFunction0D/BPy_UnaryFunction0DId.h
intern/python/UnaryFunction0D/BPy_UnaryFunction0DMaterial.cpp
intern/python/UnaryFunction0D/BPy_UnaryFunction0DMaterial.h
intern/python/UnaryFunction0D/BPy_UnaryFunction0DUnsigned.cpp
intern/python/UnaryFunction0D/BPy_UnaryFunction0DUnsigned.h
intern/python/UnaryFunction0D/BPy_UnaryFunction0DVec2f.cpp
intern/python/UnaryFunction0D/BPy_UnaryFunction0DVec2f.h
intern/python/UnaryFunction0D/BPy_UnaryFunction0DVec3f.cpp
intern/python/UnaryFunction0D/BPy_UnaryFunction0DVec3f.h
intern/python/UnaryFunction0D/BPy_UnaryFunction0DVectorViewShape.cpp
intern/python/UnaryFunction0D/BPy_UnaryFunction0DVectorViewShape.h
intern/python/UnaryFunction0D/BPy_UnaryFunction0DViewShape.cpp
intern/python/UnaryFunction0D/BPy_UnaryFunction0DViewShape.h
intern/python/UnaryFunction0D/UnaryFunction0D_Id/BPy_ShapeIdF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_Id/BPy_ShapeIdF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_Material/BPy_MaterialF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_Material/BPy_MaterialF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_Nature_EdgeNature/BPy_CurveNatureF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_Nature_EdgeNature/BPy_CurveNatureF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_Vec2f/BPy_Normal2DF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_Vec2f/BPy_Normal2DF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_Vec2f/BPy_VertexOrientation2DF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_Vec2f/BPy_VertexOrientation2DF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_Vec3f/BPy_VertexOrientation3DF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_Vec3f/BPy_VertexOrientation3DF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_ViewShape/BPy_GetOccludeeF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_ViewShape/BPy_GetOccludeeF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_ViewShape/BPy_GetShapeF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_ViewShape/BPy_GetShapeF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_double/BPy_Curvature2DAngleF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_double/BPy_Curvature2DAngleF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_double/BPy_DensityF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_double/BPy_DensityF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_double/BPy_GetProjectedXF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_double/BPy_GetProjectedXF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_double/BPy_GetProjectedYF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_double/BPy_GetProjectedYF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_double/BPy_GetProjectedZF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_double/BPy_GetProjectedZF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_double/BPy_GetXF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_double/BPy_GetXF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_double/BPy_GetYF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_double/BPy_GetYF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_double/BPy_GetZF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_double/BPy_GetZF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_double/BPy_LocalAverageDepthF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_double/BPy_LocalAverageDepthF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_double/BPy_ZDiscontinuityF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_double/BPy_ZDiscontinuityF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_float/BPy_GetCurvilinearAbscissaF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_float/BPy_GetCurvilinearAbscissaF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_float/BPy_GetParameterF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_float/BPy_GetParameterF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_float/BPy_GetViewMapGradientNormF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_float/BPy_GetViewMapGradientNormF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_float/BPy_ReadCompleteViewMapPixelF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_float/BPy_ReadCompleteViewMapPixelF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_float/BPy_ReadMapPixelF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_float/BPy_ReadMapPixelF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_float/BPy_ReadSteerableViewMapPixelF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_float/BPy_ReadSteerableViewMapPixelF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_unsigned_int/BPy_QuantitativeInvisibilityF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_unsigned_int/BPy_QuantitativeInvisibilityF0D.h
intern/python/UnaryFunction0D/UnaryFunction0D_vector_ViewShape/BPy_GetOccludersF0D.cpp
intern/python/UnaryFunction0D/UnaryFunction0D_vector_ViewShape/BPy_GetOccludersF0D.h
intern/python/UnaryFunction1D/BPy_UnaryFunction1DDouble.cpp
intern/python/UnaryFunction1D/BPy_UnaryFunction1DDouble.h
intern/python/UnaryFunction1D/BPy_UnaryFunction1DEdgeNature.cpp
intern/python/UnaryFunction1D/BPy_UnaryFunction1DEdgeNature.h
intern/python/UnaryFunction1D/BPy_UnaryFunction1DFloat.cpp
intern/python/UnaryFunction1D/BPy_UnaryFunction1DFloat.h
intern/python/UnaryFunction1D/BPy_UnaryFunction1DUnsigned.cpp
intern/python/UnaryFunction1D/BPy_UnaryFunction1DUnsigned.h
intern/python/UnaryFunction1D/BPy_UnaryFunction1DVec2f.cpp
intern/python/UnaryFunction1D/BPy_UnaryFunction1DVec2f.h
intern/python/UnaryFunction1D/BPy_UnaryFunction1DVec3f.cpp
intern/python/UnaryFunction1D/BPy_UnaryFunction1DVec3f.h
intern/python/UnaryFunction1D/BPy_UnaryFunction1DVectorViewShape.cpp
intern/python/UnaryFunction1D/BPy_UnaryFunction1DVectorViewShape.h
intern/python/UnaryFunction1D/BPy_UnaryFunction1DVoid.cpp
intern/python/UnaryFunction1D/BPy_UnaryFunction1DVoid.h
intern/python/UnaryFunction1D/UnaryFunction1D_Nature_EdgeNature/BPy_CurveNatureF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_Nature_EdgeNature/BPy_CurveNatureF1D.h
intern/python/UnaryFunction1D/UnaryFunction1D_Vec2f/BPy_Normal2DF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_Vec2f/BPy_Normal2DF1D.h
intern/python/UnaryFunction1D/UnaryFunction1D_Vec2f/BPy_Orientation2DF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_Vec2f/BPy_Orientation2DF1D.h
intern/python/UnaryFunction1D/UnaryFunction1D_Vec3f/BPy_Orientation3DF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_Vec3f/BPy_Orientation3DF1D.h
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_Curvature2DAngleF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_Curvature2DAngleF1D.h
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_DensityF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_DensityF1D.h
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_GetCompleteViewMapDensityF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_GetCompleteViewMapDensityF1D.h
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_GetDirectionalViewMapDensityF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_GetDirectionalViewMapDensityF1D.h
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_GetProjectedXF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_GetProjectedXF1D.h
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_GetProjectedYF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_GetProjectedYF1D.h
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_GetProjectedZF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_GetProjectedZF1D.h
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_GetSteerableViewMapDensityF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_GetSteerableViewMapDensityF1D.h
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_GetViewMapGradientNormF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_GetViewMapGradientNormF1D.h
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_GetXF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_GetXF1D.h
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_GetYF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_GetYF1D.h
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_GetZF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_GetZF1D.h
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_LocalAverageDepthF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_LocalAverageDepthF1D.h
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_ZDiscontinuityF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_double/BPy_ZDiscontinuityF1D.h
intern/python/UnaryFunction1D/UnaryFunction1D_unsigned_int/BPy_QuantitativeInvisibilityF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_unsigned_int/BPy_QuantitativeInvisibilityF1D.h
intern/python/UnaryFunction1D/UnaryFunction1D_vector_ViewShape/BPy_GetOccludeeF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_vector_ViewShape/BPy_GetOccludeeF1D.h
intern/python/UnaryFunction1D/UnaryFunction1D_vector_ViewShape/BPy_GetOccludersF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_vector_ViewShape/BPy_GetOccludersF1D.h
intern/python/UnaryFunction1D/UnaryFunction1D_vector_ViewShape/BPy_GetShapeF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_vector_ViewShape/BPy_GetShapeF1D.h
intern/python/UnaryFunction1D/UnaryFunction1D_void/BPy_ChainingTimeStampF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_void/BPy_ChainingTimeStampF1D.h
intern/python/UnaryFunction1D/UnaryFunction1D_void/BPy_IncrementChainingTimeStampF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_void/BPy_IncrementChainingTimeStampF1D.h
intern/python/UnaryFunction1D/UnaryFunction1D_void/BPy_TimeStampF1D.cpp
intern/python/UnaryFunction1D/UnaryFunction1D_void/BPy_TimeStampF1D.h
intern/python/UnaryPredicate0D/BPy_FalseUP0D.cpp
intern/python/UnaryPredicate0D/BPy_FalseUP0D.h
intern/python/UnaryPredicate0D/BPy_TrueUP0D.cpp
intern/python/UnaryPredicate0D/BPy_TrueUP0D.h
intern/python/UnaryPredicate1D/BPy_ContourUP1D.cpp
intern/python/UnaryPredicate1D/BPy_ContourUP1D.h
intern/python/UnaryPredicate1D/BPy_DensityLowerThanUP1D.cpp
intern/python/UnaryPredicate1D/BPy_DensityLowerThanUP1D.h
intern/python/UnaryPredicate1D/BPy_EqualToChainingTimeStampUP1D.cpp
intern/python/UnaryPredicate1D/BPy_EqualToChainingTimeStampUP1D.h
intern/python/UnaryPredicate1D/BPy_EqualToTimeStampUP1D.cpp
intern/python/UnaryPredicate1D/BPy_EqualToTimeStampUP1D.h
intern/python/UnaryPredicate1D/BPy_ExternalContourUP1D.cpp
intern/python/UnaryPredicate1D/BPy_ExternalContourUP1D.h
intern/python/UnaryPredicate1D/BPy_FalseUP1D.cpp
intern/python/UnaryPredicate1D/BPy_FalseUP1D.h
intern/python/UnaryPredicate1D/BPy_QuantitativeInvisibilityUP1D.cpp
intern/python/UnaryPredicate1D/BPy_QuantitativeInvisibilityUP1D.h
intern/python/UnaryPredicate1D/BPy_ShapeUP1D.cpp
intern/python/UnaryPredicate1D/BPy_ShapeUP1D.h
intern/python/UnaryPredicate1D/BPy_TrueUP1D.cpp
intern/python/UnaryPredicate1D/BPy_TrueUP1D.h
intern/python/UnaryPredicate1D/BPy_WithinImageBoundaryUP1D.cpp
intern/python/UnaryPredicate1D/BPy_WithinImageBoundaryUP1D.h
intern/scene_graph/DrawingStyle.h
intern/scene_graph/FrsMaterial.h
intern/scene_graph/IndexedFaceSet.cpp
intern/scene_graph/IndexedFaceSet.h
intern/scene_graph/LineRep.cpp
intern/scene_graph/LineRep.h
intern/scene_graph/Node.h
intern/scene_graph/NodeCamera.cpp
intern/scene_graph/NodeCamera.h
intern/scene_graph/NodeDrawingStyle.cpp
intern/scene_graph/NodeDrawingStyle.h
intern/scene_graph/NodeGroup.cpp
intern/scene_graph/NodeGroup.h
intern/scene_graph/NodeLight.cpp
intern/scene_graph/NodeLight.h
intern/scene_graph/NodeShape.cpp
intern/scene_graph/NodeShape.h
intern/scene_graph/NodeTransform.cpp
intern/scene_graph/NodeTransform.h
intern/scene_graph/NodeViewLayer.cpp
intern/scene_graph/NodeViewLayer.h
intern/scene_graph/OrientedLineRep.cpp
intern/scene_graph/OrientedLineRep.h
intern/scene_graph/Rep.cpp
intern/scene_graph/Rep.h
intern/scene_graph/SceneHash.cpp
intern/scene_graph/SceneHash.h
intern/scene_graph/ScenePrettyPrinter.cpp
intern/scene_graph/ScenePrettyPrinter.h
intern/scene_graph/SceneVisitor.cpp
intern/scene_graph/SceneVisitor.h
intern/scene_graph/TriangleRep.cpp
intern/scene_graph/TriangleRep.h
intern/scene_graph/VertexRep.cpp
intern/scene_graph/VertexRep.h
intern/stroke/AdvancedFunctions0D.cpp
intern/stroke/AdvancedFunctions0D.h
intern/stroke/AdvancedFunctions1D.cpp
intern/stroke/AdvancedFunctions1D.h
intern/stroke/AdvancedPredicates1D.h
intern/stroke/AdvancedStrokeShaders.cpp
intern/stroke/AdvancedStrokeShaders.h
intern/stroke/BasicStrokeShaders.cpp
intern/stroke/BasicStrokeShaders.h
intern/stroke/Canvas.cpp
intern/stroke/Canvas.h
intern/stroke/Chain.cpp
intern/stroke/Chain.h
intern/stroke/ChainingIterators.cpp
intern/stroke/ChainingIterators.h
intern/stroke/ContextFunctions.cpp
intern/stroke/ContextFunctions.h
intern/stroke/Curve.cpp
intern/stroke/Curve.h
intern/stroke/CurveAdvancedIterators.h
intern/stroke/CurveIterators.h
intern/stroke/Modifiers.h
intern/stroke/Module.h
intern/stroke/Operators.cpp
intern/stroke/Operators.h
intern/stroke/PSStrokeRenderer.cpp
intern/stroke/PSStrokeRenderer.h
intern/stroke/Predicates0D.cpp
intern/stroke/Predicates0D.h
intern/stroke/Predicates1D.cpp
intern/stroke/Predicates1D.h
intern/stroke/QInformationMap.h
intern/stroke/Stroke.cpp
intern/stroke/Stroke.h
intern/stroke/StrokeAdvancedIterators.h
intern/stroke/StrokeIO.cpp
intern/stroke/StrokeIO.h
intern/stroke/StrokeIterators.h
intern/stroke/StrokeLayer.cpp
intern/stroke/StrokeLayer.h
intern/stroke/StrokeRenderer.cpp
intern/stroke/StrokeRenderer.h
intern/stroke/StrokeRep.cpp
intern/stroke/StrokeRep.h
intern/stroke/StrokeShader.cpp
intern/stroke/StrokeShader.h
intern/stroke/StrokeTesselator.cpp
intern/stroke/StrokeTesselator.h
intern/stroke/StyleModule.h
intern/stroke/TextStrokeRenderer.cpp
intern/stroke/TextStrokeRenderer.h
intern/system/BaseIterator.h
intern/system/BaseObject.cpp
intern/system/BaseObject.h
intern/system/Cast.h
intern/system/Exception.cpp
intern/system/Exception.h
intern/system/FreestyleConfig.h
intern/system/Id.h
intern/system/Interpreter.h
intern/system/Iterator.cpp
intern/system/Iterator.h
intern/system/PointerSequence.h
intern/system/Precision.h
intern/system/ProgressBar.h
intern/system/PseudoNoise.cpp
intern/system/PseudoNoise.h
intern/system/PythonInterpreter.cpp
intern/system/PythonInterpreter.h
intern/system/RandGen.cpp
intern/system/RandGen.h
intern/system/RenderMonitor.h
intern/system/StringUtils.cpp
intern/system/StringUtils.h
intern/system/TimeStamp.cpp
intern/system/TimeStamp.h
intern/system/TimeUtils.h
intern/view_map/ArbitraryGridDensityProvider.cpp
intern/view_map/ArbitraryGridDensityProvider.h
intern/view_map/AutoPtrHelper.h
intern/view_map/AverageAreaGridDensityProvider.cpp
intern/view_map/AverageAreaGridDensityProvider.h
intern/view_map/BoxGrid.cpp
intern/view_map/BoxGrid.h
intern/view_map/CulledOccluderSource.cpp
intern/view_map/CulledOccluderSource.h
intern/view_map/FEdgeXDetector.cpp
intern/view_map/FEdgeXDetector.h
intern/view_map/Functions0D.cpp
intern/view_map/Functions0D.h
intern/view_map/Functions1D.cpp
intern/view_map/Functions1D.h
intern/view_map/GridDensityProvider.h
intern/view_map/HeuristicGridDensityProviderFactory.cpp
intern/view_map/HeuristicGridDensityProviderFactory.h
intern/view_map/Interface0D.cpp
intern/view_map/Interface0D.h
intern/view_map/Interface1D.cpp
intern/view_map/Interface1D.h
intern/view_map/OccluderSource.cpp
intern/view_map/OccluderSource.h
intern/view_map/Pow23GridDensityProvider.cpp
intern/view_map/Pow23GridDensityProvider.h
intern/view_map/Silhouette.cpp
intern/view_map/Silhouette.h
intern/view_map/SilhouetteGeomEngine.cpp
intern/view_map/SilhouetteGeomEngine.h
intern/view_map/SphericalGrid.cpp
intern/view_map/SphericalGrid.h
intern/view_map/SteerableViewMap.cpp
intern/view_map/SteerableViewMap.h
intern/view_map/ViewEdgeXBuilder.cpp
intern/view_map/ViewEdgeXBuilder.h
intern/view_map/ViewMap.cpp
intern/view_map/ViewMap.h
intern/view_map/ViewMapAdvancedIterators.h
intern/view_map/ViewMapBuilder.cpp
intern/view_map/ViewMapBuilder.h
intern/view_map/ViewMapIterators.h
intern/view_map/ViewMapTesselator.cpp
intern/view_map/ViewMapTesselator.h
intern/winged_edge/Curvature.cpp
intern/winged_edge/Curvature.h
intern/winged_edge/Nature.h
intern/winged_edge/WEdge.cpp
intern/winged_edge/WEdge.h
intern/winged_edge/WFillGrid.cpp
intern/winged_edge/WFillGrid.h
intern/winged_edge/WSFillGrid.cpp
intern/winged_edge/WSFillGrid.h
intern/winged_edge/WXEdge.cpp
intern/winged_edge/WXEdge.h
intern/winged_edge/WXEdgeBuilder.cpp
intern/winged_edge/WXEdgeBuilder.h
intern/winged_edge/WingedEdgeBuilder.cpp
intern/winged_edge/WingedEdgeBuilder.h
)
set(LIB
PRIVATE bf::blenkernel
PRIVATE bf::blenlib
PRIVATE bf::blentranslation
PRIVATE bf::depsgraph
PRIVATE bf::dna
PRIVATE bf::imbuf
PRIVATE bf::intern::guardedalloc
bf_python_mathutils
PRIVATE bf::render
PRIVATE bf::dependencies::optional::python
)
set(INC
.
../makesrna
../python
../python/intern
../render/intern
# RNA_prototypes.hh
${CMAKE_BINARY_DIR}/source/blender/makesrna
)
set(INC_SYS
)
add_definitions(-DWITH_FREESTYLE)
if(WITH_PYTHON_MODULE)
add_definitions(-DPy_ENABLE_SHARED)
endif()
if(WITH_PYTHON_SAFETY)
# For bpy_rna.hh access.
add_definitions(-DWITH_PYTHON_SAFETY)
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
# Suppress noisy warnings from GCC.
# NOTE(@ideasman42): It seems resolving these could introduce functional changes.
# Suppress unless someone is going to address them.
string(APPEND CMAKE_CXX_FLAGS " -Wno-deprecated-copy")
endif()
blender_add_lib(bf_freestyle "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
if(COMMAND target_precompile_headers)
target_precompile_headers(bf_freestyle PRIVATE FRS_precomp.h)
endif()
# RNA_prototypes.hh
add_dependencies(bf_freestyle bf_rna)

View File

@@ -0,0 +1,58 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
*/
namespace blender {
struct FreestyleConfig;
struct FreestyleLineStyle;
struct Material;
struct Render;
struct FreestyleGlobals {
struct Scene *scene;
/* camera information */
float viewpoint[3];
float mv[4][4];
float proj[4][4];
int viewport[4];
};
extern struct FreestyleGlobals g_freestyle;
/* Rendering */
void FRS_init(void);
void FRS_set_context(struct bContext *C);
int FRS_is_freestyle_enabled(struct ViewLayer *view_layer);
void FRS_init_stroke_renderer(struct Render *re);
void FRS_begin_stroke_rendering(struct Render *re);
void FRS_do_stroke_rendering(struct Render *re, struct ViewLayer *view_layer);
void FRS_end_stroke_rendering(struct Render *re);
void FRS_free_view_map_cache(void);
void FRS_composite_result(struct Render *re,
struct ViewLayer *view_layer,
struct Render *freestyle_render);
void FRS_exit(void);
/* FreestyleConfig.linesets */
void FRS_copy_active_lineset(struct FreestyleConfig *config);
void FRS_paste_active_lineset(struct FreestyleConfig *config);
void FRS_delete_active_lineset(struct FreestyleConfig *config);
/**
* Reinsert the active lineset at an offset \a direction from current position.
* \return if position of active lineset has changed.
*/
bool FRS_move_active_lineset(struct FreestyleConfig *config, int direction);
/* Testing */
struct Material *FRS_create_stroke_material(struct Main *bmain,
struct FreestyleLineStyle *linestyle);
} // namespace blender

View File

@@ -0,0 +1,53 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/* Pre-compiled headers, see: D2606. */
#include <Python.h>
#include <algorithm>
#include <deque>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <math.h>
#include <memory>
#include <set>
#include <sstream>
#include <stack>
#include <stdarg.h>
#include <stdbool.h>
#include <string>
#include <time.h>
#include <vector>
#include "intern/python/BPy_BBox.h"
#include "intern/python/BPy_BinaryPredicate0D.h"
#include "intern/python/BPy_BinaryPredicate1D.h"
#include "intern/python/BPy_ContextFunctions.h"
#include "intern/python/BPy_Convert.h"
#include "intern/python/BPy_Freestyle.h"
#include "intern/python/BPy_FrsMaterial.h"
#include "intern/python/BPy_FrsNoise.h"
#include "intern/python/BPy_Id.h"
#include "intern/python/BPy_IntegrationType.h"
#include "intern/python/BPy_Interface0D.h"
#include "intern/python/BPy_Interface1D.h"
#include "intern/python/BPy_Iterator.h"
#include "intern/python/BPy_MediumType.h"
#include "intern/python/BPy_Nature.h"
#include "intern/python/BPy_Operators.h"
#include "intern/python/BPy_SShape.h"
#include "intern/python/BPy_StrokeAttribute.h"
#include "intern/python/BPy_StrokeShader.h"
#include "intern/python/BPy_UnaryFunction0D.h"
#include "intern/python/BPy_UnaryFunction1D.h"
#include "intern/python/BPy_UnaryPredicate0D.h"
#include "intern/python/BPy_UnaryPredicate1D.h"
#include "intern/python/BPy_ViewMap.h"
#include "intern/python/BPy_ViewShape.h"
#include "intern/python/Director.h"

View File

@@ -0,0 +1,220 @@
/* SPDX-FileCopyrightText: 2008-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "AppCanvas.h"
#include "AppConfig.h"
#include "AppView.h"
#include "Controller.h"
#include "../image/Image.h"
#include "../stroke/StrokeRenderer.h"
#include "../stroke/StyleModule.h"
#include "../system/TimeStamp.h"
#include "../system/StringUtils.h"
namespace Freestyle {
AppCanvas::AppCanvas()
{
_pViewer = nullptr;
_MapsPath = Config::Path::getInstance()->getMapsDir().c_str();
}
AppCanvas::AppCanvas(AppView *iViewer)
{
_pViewer = iViewer;
}
AppCanvas::AppCanvas(const AppCanvas &iBrother) : Canvas(iBrother)
{
_pViewer = iBrother._pViewer;
}
AppCanvas::~AppCanvas()
{
_pViewer = nullptr;
}
void AppCanvas::setViewer(AppView *iViewer)
{
_pViewer = iViewer;
}
int AppCanvas::width() const
{
return _pViewer->width();
}
int AppCanvas::height() const
{
return _pViewer->height();
}
BBox<Vec2i> AppCanvas::border() const
{
return _pViewer->border();
}
float AppCanvas::thickness() const
{
return _pViewer->thickness();
}
BBox<Vec3r> AppCanvas::scene3DBBox() const
{
return _pViewer->scene3DBBox();
}
void AppCanvas::preDraw()
{
Canvas::preDraw();
}
void AppCanvas::init()
{
#if 0
static bool firsttime = true;
if (firsttime) {
_Renderer = new BlenderStrokeRenderer;
if (!StrokeRenderer::loadTextures()) {
cerr << "unable to load stroke textures" << endl;
return;
}
}
#endif
}
void AppCanvas::postDraw()
{
for (uint i = 0; i < _StyleModules.size(); i++) {
if (!_StyleModules[i]->getDisplayed() || !_Layers[i]) {
continue;
}
_Layers[i]->ScaleThickness(thickness());
}
Canvas::postDraw();
}
void AppCanvas::Erase()
{
Canvas::Erase();
}
// Abstract
void AppCanvas::readColorPixels(int x, int y, int w, int h, RGBImage &oImage) const
{
float *rgb = new float[3 * w * h];
memset(rgb, 0, sizeof(float[3]) * w * h);
int xsch = width();
int ysch = height();
if (_pass_diffuse.buf) {
int xmin = border().getMin().x();
int ymin = border().getMin().y();
int xmax = border().getMax().x();
int ymax = border().getMax().y();
int rectx = _pass_diffuse.width;
int recty = _pass_diffuse.height;
float xfac = float(rectx) / float(xmax - xmin);
float yfac = float(recty) / float(ymax - ymin);
#if 0
if (blender::G.debug & blender::G_DEBUG_FREESTYLE) {
printf("readColorPixels %d x %d @ (%d, %d) in %d x %d [%d x %d] -- %d x %d @ %d%%\n",
w,
h,
x,
y,
xsch,
ysch,
xmax - xmin,
ymax - ymin,
rectx,
recty,
int(xfac * 100.0f));
}
#endif
int ii, jj;
for (int j = 0; j < h; j++) {
jj = int((y - ymin + j) * yfac);
if (jj < 0 || jj >= recty) {
continue;
}
for (int i = 0; i < w; i++) {
ii = int((x - xmin + i) * xfac);
if (ii < 0 || ii >= rectx) {
continue;
}
memcpy(rgb + (w * j + i) * 3, _pass_diffuse.buf + (rectx * jj + ii) * 3, sizeof(float[3]));
}
}
}
oImage.setArray(rgb, xsch, ysch, w, h, x, y, false);
}
void AppCanvas::readDepthPixels(int x, int y, int w, int h, GrayImage &oImage) const
{
float *z = new float[w * h];
memset(z, 0, sizeof(float) * w * h);
int xsch = width();
int ysch = height();
if (_pass_z.buf) {
int xmin = border().getMin().x();
int ymin = border().getMin().y();
int xmax = border().getMax().x();
int ymax = border().getMax().y();
int rectx = _pass_z.width;
int recty = _pass_z.height;
float xfac = float(rectx) / float(xmax - xmin);
float yfac = float(recty) / float(ymax - ymin);
#if 0
if (blender::G.debug & blender::G_DEBUG_FREESTYLE) {
printf("readDepthPixels %d x %d @ (%d, %d) in %d x %d [%d x %d] -- %d x %d @ %d%%\n",
w,
h,
x,
y,
xsch,
ysch,
xmax - xmin,
ymax - ymin,
rectx,
recty,
int(xfac * 100.0f));
}
#endif
int ii, jj;
for (int j = 0; j < h; j++) {
jj = int((y - ymin + j) * yfac);
if (jj < 0 || jj >= recty) {
continue;
}
for (int i = 0; i < w; i++) {
ii = int((x - xmin + i) * xfac);
if (ii < 0 || ii >= rectx) {
continue;
}
z[w * j + i] = _pass_z.buf[rectx * jj + ii];
}
}
}
oImage.setArray(z, xsch, ysch, w, h, x, y, false);
}
void AppCanvas::RenderStroke(Stroke *iStroke)
{
if (_basic) {
iStroke->RenderBasic(_Renderer);
}
else {
iStroke->Render(_Renderer);
}
}
void AppCanvas::update() {}
} /* namespace Freestyle */

View File

@@ -0,0 +1,82 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
*/
#include "../stroke/Canvas.h"
#include "AppView.h"
namespace Freestyle {
class AppCanvas : public Canvas {
public:
AppCanvas();
AppCanvas(AppView *iViewer);
AppCanvas(const AppCanvas &iBrother);
virtual ~AppCanvas();
/** operations that need to be done before a draw */
virtual void preDraw();
/** operations that need to be done after a draw */
virtual void postDraw();
/** Erases the layers and clears the canvas */
virtual void Erase();
/* init the canvas */
virtual void init();
/** Reads a pixel area from the canvas */
virtual void readColorPixels(int x, int y, int w, int h, RGBImage &oImage) const;
/** Reads a depth pixel area from the canvas */
virtual void readDepthPixels(int x, int y, int w, int h, GrayImage &oImage) const;
virtual BBox<Vec3r> scene3DBBox() const;
/* abstract */
virtual void RenderStroke(Stroke *);
virtual void update();
/** accessors */
virtual int width() const;
virtual int height() const;
virtual BBox<Vec2i> border() const;
virtual float thickness() const;
AppView *_pViewer;
inline const AppView *viewer() const
{
return _pViewer;
}
/** modifiers */
void setViewer(AppView *iViewer);
/* soc */
void setPassDiffuse(float *buf, int width, int height)
{
_pass_diffuse.buf = buf;
_pass_diffuse.width = width;
_pass_diffuse.height = height;
}
void setPassZ(float *buf, int width, int height)
{
_pass_z.buf = buf;
_pass_z.width = width;
_pass_z.height = height;
}
private:
struct {
float *buf;
int width, height;
} _pass_diffuse, _pass_z;
};
} /* namespace Freestyle */

View File

@@ -0,0 +1,76 @@
/* SPDX-FileCopyrightText: 2008-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "AppConfig.h"
#include <iostream>
#include "../system/FreestyleConfig.h"
#include "../system/StringUtils.h"
using namespace std;
#include "BKE_appdir.hh"
namespace Freestyle::Config {
Path *Path::_pInstance = nullptr;
Path::Path()
{
// get the root directory
// soc
const std::optional<std::string> path = BKE_appdir_folder_id(blender::BLENDER_SYSTEM_SCRIPTS,
nullptr);
setRootDir(path.value_or(blender::BKE_appdir_program_dir()));
_pInstance = this;
}
void Path::setRootDir(const string &iRootDir)
{
_ProjectDir = iRootDir + string(DIR_SEP) + "freestyle";
_ModelsPath = "";
_PatternsPath = _ProjectDir + string(DIR_SEP) + "data" + string(DIR_SEP) + "textures" +
string(DIR_SEP) + "variation_patterns" + string(DIR_SEP);
_BrushesPath = _ProjectDir + string(DIR_SEP) + "data" + string(DIR_SEP) + "textures" +
string(DIR_SEP) + "brushes" + string(DIR_SEP);
_EnvMapDir = _ProjectDir + string(DIR_SEP) + "data" + string(DIR_SEP) + "env_map" +
string(DIR_SEP);
_MapsDir = _ProjectDir + string(DIR_SEP) + "data" + string(DIR_SEP) + "maps" + string(DIR_SEP);
}
void Path::setHomeDir(const string &iHomeDir)
{
_HomeDir = iHomeDir;
}
Path::~Path()
{
_pInstance = nullptr;
}
Path *Path::getInstance()
{
return _pInstance;
}
string Path::getEnvVar(const string &iEnvVarName)
{
string value;
if (!getenv(iEnvVarName.c_str())) {
cerr << "Warning: You may want to set the $" << iEnvVarName
<< " environment variable to use Freestyle." << endl
<< " Otherwise, the current directory will be used instead." << endl;
value = ".";
}
else {
value = getenv(iEnvVarName.c_str());
}
return value;
}
} // namespace Freestyle::Config

View File

@@ -0,0 +1,104 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Configuration file
*/
#include <algorithm>
#include <string>
#include "../system/Precision.h"
#include "MEM_guardedalloc.h"
using namespace std;
namespace Freestyle {
namespace Config {
class Path {
protected:
static Path *_pInstance;
string _ProjectDir;
string _ModelsPath;
string _PatternsPath;
string _BrushesPath;
string _EnvMapDir;
string _MapsDir;
string _HomeDir;
public:
Path();
virtual ~Path();
static Path *getInstance();
void setRootDir(const string &iRootDir);
void setHomeDir(const string &iHomeDir);
const string &getProjectDir() const
{
return _ProjectDir;
}
const string &getModelsPath() const
{
return _ModelsPath;
}
const string &getPatternsPath() const
{
return _PatternsPath;
}
const string &getBrushesPath() const
{
return _BrushesPath;
}
const string &getEnvMapDir() const
{
return _EnvMapDir;
}
const string &getMapsDir() const
{
return _MapsDir;
}
const string &getHomeDir() const
{
return _HomeDir;
}
static string getEnvVar(const string &iEnvVarName);
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:Config:Path")
};
//
// Configuration, default values
//
//////////////////////////////////////////////////////////////
// Application
static const string APPLICATION_NAME("APPNAME");
static const string APPLICATION_VERSION("APPVERSION");
// ViewMap
static const string VIEWMAP_EXTENSION("vm");
static const string VIEWMAP_MAGIC("ViewMap File");
static const string VIEWMAP_VERSION("1.9");
// Style modules
static const string STYLE_MODULE_EXTENSION("py");
static const string STYLE_MODULES_LIST_EXTENSION("sml");
// Options
static const string OPTIONS_DIR("." + APPLICATION_NAME);
static const string OPTIONS_FILE("options.xml");
static const string OPTIONS_CURRENT_DIRS_FILE("current_dirs.xml");
static const string OPTIONS_QGLVIEWER_FILE("qglviewer.xml");
} // namespace Config
} /* namespace Freestyle */

View File

@@ -0,0 +1,162 @@
/* SPDX-FileCopyrightText: 2008-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include <iostream>
#include "AppConfig.h"
#include "AppView.h"
#include "Controller.h"
#include "../scene_graph/LineRep.h"
#include "../scene_graph/NodeLight.h"
#include "../scene_graph/NodeShape.h"
#include "../scene_graph/VertexRep.h"
#include "../stroke/Canvas.h"
#include "../system/StringUtils.h"
#include "../view_map/Silhouette.h"
#include "../view_map/ViewMap.h"
#include "BLI_math_rotation.h"
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
#if 1 // FRS_antialiasing
# include "BKE_global.hh"
# include "DNA_scene_types.h"
#endif
#include "FRS_freestyle.h"
namespace Freestyle {
AppView::AppView(const char * /*iName*/)
{
_Fovy = DEG2RADF(30.0f);
_ModelRootNode = new NodeDrawingStyle;
_SilhouetteRootNode = new NodeDrawingStyle;
_DebugRootNode = new NodeDrawingStyle;
_RootNode.AddChild(_ModelRootNode);
_SilhouetteRootNode->setStyle(DrawingStyle::LINES);
_SilhouetteRootNode->setLightingEnabled(false);
_SilhouetteRootNode->setLineWidth(2.0f);
_SilhouetteRootNode->setPointSize(3.0f);
_RootNode.AddChild(_SilhouetteRootNode);
_DebugRootNode->setStyle(DrawingStyle::LINES);
_DebugRootNode->setLightingEnabled(false);
_DebugRootNode->setLineWidth(1.0f);
_RootNode.AddChild(_DebugRootNode);
_minBBox = std::min(
std::min(_ModelRootNode->bbox().getMin()[0], _ModelRootNode->bbox().getMin()[1]),
_ModelRootNode->bbox().getMin()[2]);
_maxBBox = std::max(
std::max(_ModelRootNode->bbox().getMax()[0], _ModelRootNode->bbox().getMax()[1]),
_ModelRootNode->bbox().getMax()[2]);
_maxAbs = std::max(rabs(_minBBox), rabs(_maxBBox));
_minAbs = std::min(rabs(_minBBox), rabs(_maxBBox));
_p2DSelectionNode = new NodeDrawingStyle;
_p2DSelectionNode->setLightingEnabled(false);
_p2DSelectionNode->setStyle(DrawingStyle::LINES);
_p2DSelectionNode->setLineWidth(5.0f);
_p2DNode.AddChild(_p2DSelectionNode);
NodeLight *light = new NodeLight;
_Light.AddChild(light);
}
AppView::~AppView()
{
/*int ref =*//* UNUSED */ _RootNode.destroy();
_Light.destroy();
/*ref =*//* UNUSED */ _p2DNode.destroy();
}
real AppView::distanceToSceneCenter()
{
BBox<Vec3r> bbox = _ModelRootNode->bbox();
Vec3r v(UNPACK3(blender::g_freestyle.viewpoint));
v -= 0.5 * (bbox.getMin() + bbox.getMax());
return v.norm();
}
real AppView::znear()
{
BBox<Vec3r> bbox = _ModelRootNode->bbox();
Vec3r u = bbox.getMin();
Vec3r v = bbox.getMax();
Vec3r cameraCenter(UNPACK3(blender::g_freestyle.viewpoint));
Vec3r w1(u[0], u[1], u[2]);
Vec3r w2(v[0], u[1], u[2]);
Vec3r w3(u[0], v[1], u[2]);
Vec3r w4(v[0], v[1], u[2]);
Vec3r w5(u[0], u[1], v[2]);
Vec3r w6(v[0], u[1], v[2]);
Vec3r w7(u[0], v[1], v[2]);
Vec3r w8(v[0], v[1], v[2]);
real _znear = std::min(
(w1 - cameraCenter).norm(),
std::min((w2 - cameraCenter).norm(),
std::min((w3 - cameraCenter).norm(),
std::min((w4 - cameraCenter).norm(),
std::min((w5 - cameraCenter).norm(),
std::min((w6 - cameraCenter).norm(),
std::min((w7 - cameraCenter).norm(),
(w8 - cameraCenter).norm())))))));
return std::max(_znear, 0.001);
}
real AppView::zfar()
{
BBox<Vec3r> bbox = _ModelRootNode->bbox();
Vec3r u = bbox.getMin();
Vec3r v = bbox.getMax();
Vec3r cameraCenter(UNPACK3(blender::g_freestyle.viewpoint));
Vec3r w1(u[0], u[1], u[2]);
Vec3r w2(v[0], u[1], u[2]);
Vec3r w3(u[0], v[1], u[2]);
Vec3r w4(v[0], v[1], u[2]);
Vec3r w5(u[0], u[1], v[2]);
Vec3r w6(v[0], u[1], v[2]);
Vec3r w7(u[0], v[1], v[2]);
Vec3r w8(v[0], v[1], v[2]);
real _zfar = std::max(
(w1 - cameraCenter).norm(),
std::max((w2 - cameraCenter).norm(),
std::max((w3 - cameraCenter).norm(),
std::max((w4 - cameraCenter).norm(),
std::max((w5 - cameraCenter).norm(),
std::max((w6 - cameraCenter).norm(),
std::max((w7 - cameraCenter).norm(),
(w8 - cameraCenter).norm())))))));
return _zfar;
}
real AppView::GetFocalLength()
{
real Near = std::max(0.1, (real)(-2.0f * _maxAbs + distanceToSceneCenter()));
return Near;
}
} /* namespace Freestyle */

View File

@@ -0,0 +1,257 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
*/
#include "AppConfig.h"
#include "../geometry/BBox.h"
#include "../geometry/Geom.h"
#include "../scene_graph/NodeDrawingStyle.h"
#include "../system/Precision.h"
#include "BLI_math_base.h"
#include "MEM_guardedalloc.h"
namespace Freestyle {
using namespace Geometry;
class AppView {
public:
AppView(const char *iName = 0);
virtual ~AppView();
public:
// inherited
inline uint width()
{
return _width;
}
inline uint height()
{
return _height;
}
inline BBox<Vec2i> border()
{
return _border;
}
inline float thickness()
{
return _thickness;
}
inline void setWidth(uint width)
{
_width = width;
}
inline void setHeight(uint height)
{
_height = height;
}
inline void setBorder(int xmin, int ymin, int xmax, int ymax)
{
_border = BBox<Vec2i>(Vec2i(xmin, ymin), Vec2i(xmax, ymax));
}
inline void setThickness(float thickness)
{
_thickness = thickness;
}
protected:
uint _width, _height;
BBox<Vec2i> _border;
float _thickness;
public:
/** Sets the model to draw in the viewer
* iModel
* The Root Node of the model
*/
inline void setModel(NodeGroup *iModel)
{
if (0 != _ModelRootNode->numberOfChildren()) {
_ModelRootNode->DetachChildren();
_ModelRootNode->clearBBox();
}
AddModel(iModel);
}
/** Adds a model for displaying in the viewer */
inline void AddModel(NodeGroup *iModel)
{
_ModelRootNode->AddChild(iModel);
_ModelRootNode->UpdateBBox();
_minBBox = std::min(
std::min(_ModelRootNode->bbox().getMin()[0], _ModelRootNode->bbox().getMin()[1]),
_ModelRootNode->bbox().getMin()[2]);
_maxBBox = std::max(
std::max(_ModelRootNode->bbox().getMax()[0], _ModelRootNode->bbox().getMax()[1]),
_ModelRootNode->bbox().getMax()[2]);
_maxAbs = std::max(rabs(_minBBox), rabs(_maxBBox));
_minAbs = std::min(rabs(_minBBox), rabs(_maxBBox));
}
inline void AddSilhouette(NodeGroup *iSilhouette)
{
_SilhouetteRootNode->AddChild(iSilhouette);
}
inline void Add2DSilhouette(NodeGroup * /*iSilhouette*/)
{
//_pFENode->AddChild(iSilhouette);
}
inline void Add2DVisibleSilhouette(NodeGroup * /*iVSilhouette*/)
{
//_pVisibleSilhouetteNode->AddChild(iVSilhouette);
}
inline void setDebug(NodeGroup *iDebug)
{
if (0 != _DebugRootNode->numberOfChildren()) {
_DebugRootNode->DetachChildren();
_DebugRootNode->clearBBox();
}
AddDebug(iDebug);
}
inline void AddDebug(NodeGroup *iDebug)
{
_DebugRootNode->AddChild(iDebug);
}
inline void DetachModel(Node *iModel)
{
_ModelRootNode->DetachChild(iModel);
_ModelRootNode->UpdateBBox();
_minBBox = std::min(
std::min(_ModelRootNode->bbox().getMin()[0], _ModelRootNode->bbox().getMin()[1]),
_ModelRootNode->bbox().getMin()[2]);
_maxBBox = std::max(
std::max(_ModelRootNode->bbox().getMax()[0], _ModelRootNode->bbox().getMax()[1]),
_ModelRootNode->bbox().getMax()[2]);
_maxAbs = std::max(rabs(_minBBox), rabs(_maxBBox));
_minAbs = std::min(rabs(_minBBox), rabs(_maxBBox));
}
inline void DetachModel()
{
_ModelRootNode->DetachChildren();
_ModelRootNode->clearBBox();
#if 0
// 2D Scene
_p2DNode.DetachChildren();
_pFENode->DetachChildren();
_pVisibleSilhouetteNode->DetachChildren();
#endif
}
inline void DetachSilhouette()
{
_SilhouetteRootNode->DetachChildren();
#if 0
_pFENode->DetachChildren();
_pVisibleSilhouetteNode->DetachChildren();
#endif
_p2DSelectionNode->destroy();
}
inline void DetachVisibleSilhouette()
{
//_pVisibleSilhouetteNode->DetachChildren();
_p2DSelectionNode->destroy();
}
inline void DetachDebug()
{
_DebugRootNode->DetachChildren();
}
real distanceToSceneCenter();
real GetFocalLength();
inline real GetAspect() const
{
return ((real)_width / (real)_height);
}
void setHorizontalFov(float hfov)
{
_Fovy = 2.0 * atan(tan(hfov / 2.0) / GetAspect());
}
inline real GetFovyRadian() const
{
return _Fovy;
}
inline real GetFovyDegrees() const
{
return _Fovy * 180.0 / M_PI; /* TODO: Use RAD2DEG here too? */
}
BBox<Vec3r> scene3DBBox() const
{
return _ModelRootNode->bbox();
}
real znear();
real zfar();
public:
/** Core scene drawing */
void DrawScene(SceneVisitor *iRenderer);
/** 2D Scene Drawing */
void Draw2DScene(SceneVisitor *iRenderer);
protected:
/** fabs or abs */
inline int rabs(int x)
{
return abs(x);
}
inline real rabs(real x)
{
return fabs(x);
}
protected:
float _Fovy;
// The root node container
NodeGroup _RootNode;
NodeDrawingStyle *_ModelRootNode;
NodeDrawingStyle *_SilhouetteRootNode;
NodeDrawingStyle *_DebugRootNode;
NodeGroup _Light;
real _minBBox;
real _maxBBox;
real _maxAbs;
real _minAbs;
// 2D Scene
bool _Draw2DScene;
bool _Draw3DScene;
NodeGroup _p2DNode;
NodeDrawingStyle *_p2DSelectionNode;
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:AppView")
};
} /* namespace Freestyle */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,269 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief The spinal tap of the system.
*/
#include <string>
#include "../geometry/FastGrid.h"
#include "../scene_graph/SceneHash.h"
#include "../system/Precision.h"
#include "../system/TimeUtils.h"
#include "../view_map/FEdgeXDetector.h"
#include "../view_map/ViewMapBuilder.h"
#include "MEM_guardedalloc.h"
namespace blender {
struct Depsgraph;
struct Render;
struct ViewLayer;
} // namespace blender
namespace Freestyle {
class AppCanvas;
class AppView;
class Interpreter;
class NodeGroup;
class ProgressBar;
class RenderMonitor;
class SShape;
class ViewEdge;
class ViewMap;
class Controller {
public:
Controller();
~Controller();
void setView(AppView *iView);
void setRenderMonitor(RenderMonitor *iRenderMonitor);
void setPassDiffuse(float *buf, int width, int height);
void setPassZ(float *buf, int width, int height);
void setContext(blender::bContext *C);
// soc
void init_options();
int LoadMesh(blender::Render *re, blender::ViewLayer *view_layer, blender::Depsgraph *depsgraph);
int Load3DSFile(const char *iFileName);
void CloseFile();
void ComputeViewMap();
void ComputeSteerableViewMap();
void saveSteerableViewMapImages();
void toggleEdgeTesselationNature(Nature::EdgeNature iNature);
int DrawStrokes();
void ResetRenderCount();
blender::Render *RenderStrokes(blender::Render *re, bool render);
void SwapStyleModules(uint i1, uint i2);
void InsertStyleModule(uint index, const char *iFileName);
void InsertStyleModule(uint index, const char *iName, const char *iBuffer);
void InsertStyleModule(uint index, const char *iName, struct blender::Text *iText);
void AddStyleModule(const char *iFileName);
void RemoveStyleModule(uint index);
void ReloadStyleModule(uint index, const char *iFileName);
void Clear();
void ClearRootNode();
void DeleteWingedEdge();
void DeleteViewMap(bool freeCache = false);
void toggleLayer(uint index, bool iDisplay);
void setModified(uint index, bool iMod);
void resetModified(bool iMod = false);
void updateCausalStyleModules(uint index);
void displayDensityCurves(int x, int y);
ViewEdge *SelectViewEdge(real x, real y);
FEdge *SelectFEdge(real x, real y);
NodeGroup *BuildRep(vector<ViewEdge *>::iterator vedges_begin,
vector<ViewEdge *>::iterator vedges_end);
#if 0
NodeGroup *debugNode()
{
return _DebugNode;
}
AppView *view()
{
return _pView;
}
NodeGroup *debugScene()
{
return _DebugNode;
}
Grid &grid()
{
return _Grid;
}
#endif
void toggleVisibilityAlgo();
void setVisibilityAlgo(int algo);
int getVisibilityAlgo();
void setViewMapCache(bool iBool);
bool getViewMapCache() const;
void setQuantitativeInvisibility(bool iBool); // if true, we compute quantitativeInvisibility
bool getQuantitativeInvisibility() const;
void setFaceSmoothness(bool iBool);
bool getFaceSmoothness() const;
void setComputeRidgesAndValleysFlag(bool b);
bool getComputeRidgesAndValleysFlag() const;
void setComputeSuggestiveContoursFlag(bool b);
bool getComputeSuggestiveContoursFlag() const;
void setComputeMaterialBoundariesFlag(bool b);
bool getComputeMaterialBoundariesFlag() const;
void setComputeSteerableViewMapFlag(bool iBool);
bool getComputeSteerableViewMapFlag() const;
void setCreaseAngle(float angle)
{
_creaseAngle = angle;
}
float getCreaseAngle() const
{
return _creaseAngle;
}
void setSphereRadius(float s)
{
_sphereRadius = s;
}
float getSphereRadius() const
{
return _sphereRadius;
}
void setSuggestiveContourKrDerivativeEpsilon(float dkr)
{
_suggestiveContourKrDerivativeEpsilon = dkr;
}
float getSuggestiveContourKrDerivativeEpsilon() const
{
return _suggestiveContourKrDerivativeEpsilon;
}
void setModelsDir(const string &dir);
string getModelsDir() const;
void setModulesDir(const string &dir);
string getModulesDir() const;
bool hitViewMapCache();
void resetInterpreter();
public:
// Viewmap data structure
ViewMap *_ViewMap;
// Canvas
AppCanvas *_Canvas;
private:
// Main Window:
// AppMainWindow *_pMainWindow;
// List of models currently loaded
vector<string> _ListOfModels;
// Current directories
// ConfigIO* _current_dirs;
// View
// 3D
AppView *_pView;
// 2D
#if 0
Viewer2DWindow *_pView2DWindow;
Viewer2D *_pView2D;
#endif
RenderMonitor *_pRenderMonitor;
// Model
// Drawing Structure
NodeGroup *_RootNode;
// Winged-Edge structure
WingedEdge *_winged_edge;
#if 0
// Silhouette structure:
std::vector<SShape *> _SShapes;
NodeGroup *_SRoot;
// Silhouette
NodeGroup *_SilhouetteNode;
NodeGroup *_ProjectedSilhouette;
NodeGroup *_VisibleProjectedSilhouette;
// more Debug info
NodeGroup *_DebugNode;
#endif
// debug
// NodeUser<ViewMap> *_ViewMapNode; // FIXME
// Chronometer:
Chronometer _Chrono;
// Progress Bar
ProgressBar *_ProgressBar;
// edges tesselation nature
int _edgeTesselationNature;
FastGrid _Grid;
// HashGrid _Grid;
BBox<Vec3r> _Scene3dBBox;
uint _SceneNumFaces;
#if 0
real _minEdgeSize;
#endif
real _EPSILON;
real _bboxDiag;
int _render_count;
// AppStyleWindow *_pStyleWindow;
// AppOptionsWindow *_pOptionsWindow;
// AppDensityCurvesWindow *_pDensityCurvesWindow;
ViewMapBuilder::visibility_algo _VisibilityAlgo;
// Script Interpreter
Interpreter *_inter;
string _help_index;
string _browser_cmd;
bool _EnableViewMapCache;
bool _EnableQI;
bool _EnableFaceSmoothness;
bool _ComputeRidges;
bool _ComputeSuggestive;
bool _ComputeMaterialBoundaries;
float _creaseAngle;
float _sphereRadius;
float _suggestiveContourKrDerivativeEpsilon;
bool _ComputeSteerableViewMap;
FEdgeXDetector edgeDetector;
SceneHash sceneHashFunc;
real prevSceneHash;
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:Controller")
};
extern Controller *g_pController;
} /* namespace Freestyle */

View File

@@ -0,0 +1,830 @@
/* SPDX-FileCopyrightText: 2008-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BlenderFileLoader.h"
#include "DNA_layer_types.h"
#include "DNA_meshdata_types.h"
#include "BLI_math_geom.h"
#include "BLI_math_matrix.h"
#include "BLI_math_vector.h"
#include "BLI_utildefines.h"
#include "BKE_attribute.hh"
#include "BKE_customdata.hh"
#include "BKE_global.hh"
#include "BKE_mesh.hh"
#include "BKE_object.hh"
#include <sstream>
using blender::float3;
using blender::Span;
namespace Freestyle {
BlenderFileLoader::BlenderFileLoader(blender::Render *re,
blender::ViewLayer *view_layer,
blender::Depsgraph *depsgraph)
{
_re = re;
_depsgraph = depsgraph;
_Scene = nullptr;
_numFacesRead = 0;
#if 0
_minEdgeSize = DBL_MAX;
#endif
_smooth = (view_layer->freestyle_config.flags & blender::FREESTYLE_FACE_SMOOTHNESS_FLAG) != 0;
_pRenderMonitor = nullptr;
}
BlenderFileLoader::~BlenderFileLoader()
{
_Scene = nullptr;
}
NodeGroup *BlenderFileLoader::Load()
{
if (blender::G.debug & blender::G_DEBUG_FREESTYLE) {
cout << "\n=== Importing triangular meshes into Blender ===" << endl;
}
// creation of the scene root node
_Scene = new NodeGroup;
if (_re->clip_start < 0.0f) {
// Adjust clipping start/end and set up a Z offset when the viewport preview
// is used with the orthographic view. In this case, _re->clip_start is negative,
// while Freestyle assumes that imported mesh data are in the camera coordinate
// system with the view point located at origin [bug #36009].
_z_near = -0.001f;
_z_offset = _re->clip_start + _z_near;
_z_far = -_re->clip_end + _z_offset;
}
else {
_z_near = -_re->clip_start;
_z_far = -_re->clip_end;
_z_offset = 0.0f;
}
int id = 0;
const blender::eEvaluationMode eval_mode = blender::DEG_get_mode(_depsgraph);
using blender::DEG_iterator_objects_end;
using blender::DEGObjectIterData;
using blender::Object;
blender::DEGObjectIterSettings deg_iter_settings{};
deg_iter_settings.depsgraph = _depsgraph;
deg_iter_settings.flags = blender::DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY |
blender::DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET |
blender::DEG_ITER_OBJECT_FLAG_VISIBLE |
blender::DEG_ITER_OBJECT_FLAG_DUPLI;
DEG_OBJECT_ITER_BEGIN (&deg_iter_settings, ob) {
if (_pRenderMonitor && _pRenderMonitor->testBreak()) {
break;
}
if ((ob->base_flag & (blender::BASE_HOLDOUT | blender::BASE_INDIRECT_ONLY)) ||
(ob->visibility_flag & blender::OB_HOLDOUT))
{
continue;
}
if (!(BKE_object_visibility(ob, eval_mode) & blender::OB_VISIBLE_SELF)) {
continue;
}
/* Evaluated meta-balls will appear as mesh objects in the iterator. */
if (ob->type == blender::OB_MBALL) {
continue;
}
blender::Mesh *mesh = BKE_object_to_mesh(nullptr, ob, false);
if (mesh) {
insertShapeNode(ob, mesh, ++id);
BKE_object_to_mesh_clear(ob);
}
}
DEG_OBJECT_ITER_END;
// Return the built scene.
return _Scene;
}
#define CLIPPED_BY_NEAR -1
#define NOT_CLIPPED 0
#define CLIPPED_BY_FAR 1
// check if each vertex of a triangle (V1, V2, V3) is clipped by the near/far plane
// and calculate the number of triangles to be generated by clipping
int BlenderFileLoader::countClippedFaces(float v1[3], float v2[3], float v3[3], int clip[3])
{
float *v[3];
int numClipped, sum, numTris = 0;
v[0] = v1;
v[1] = v2;
v[2] = v3;
numClipped = sum = 0;
for (int i = 0; i < 3; i++) {
if (v[i][2] > _z_near) {
clip[i] = CLIPPED_BY_NEAR;
numClipped++;
}
else if (v[i][2] < _z_far) {
clip[i] = CLIPPED_BY_FAR;
numClipped++;
}
else {
clip[i] = NOT_CLIPPED;
}
#if 0
if (blender::G.debug & blender::G_DEBUG_FREESTYLE) {
printf("%d %s\n",
i,
(clip[i] == NOT_CLIPPED) ? "not" :
(clip[i] == CLIPPED_BY_NEAR) ? "near" :
"far");
}
#endif
sum += clip[i];
}
switch (numClipped) {
case 0:
numTris = 1; // triangle
break;
case 1:
numTris = 2; // tetragon
break;
case 2:
if (sum == 0) {
numTris = 3; // pentagon
}
else {
numTris = 1; // triangle
}
break;
case 3:
if (ELEM(sum, 3, -3)) {
numTris = 0;
}
else {
numTris = 2; // tetragon
}
break;
}
return numTris;
}
// find the intersection point C between the line segment from V1 to V2 and
// a clipping plane at depth Z (i.e., the Z component of C is known, while
// the X and Y components are unknown).
void BlenderFileLoader::clipLine(float v1[3], float v2[3], float c[3], float z)
{
// Order v1 and v2 by Z values to make sure that clipLine(P, Q, c, z)
// and clipLine(Q, P, c, z) gives exactly the same numerical result.
float *p, *q;
if (v1[2] < v2[2]) {
p = v1;
q = v2;
}
else {
p = v2;
q = v1;
}
double d[3];
for (int i = 0; i < 3; i++) {
d[i] = q[i] - p[i];
}
double t = (z - p[2]) / d[2];
c[0] = p[0] + t * d[0];
c[1] = p[1] + t * d[1];
c[2] = z;
}
// clip the triangle (V1, V2, V3) by the near and far clipping plane and
// obtain a set of vertices after the clipping. The number of vertices
// is at most 5.
void BlenderFileLoader::clipTriangle(int numTris,
float triCoords[][3],
float v1[3],
float v2[3],
float v3[3],
float triNormals[][3],
float n1[3],
float n2[3],
float n3[3],
bool edgeMarks[5],
bool em1,
bool em2,
bool em3,
const int clip[3])
{
float *v[3], *n[3];
bool em[3];
int i, j, k;
v[0] = v1;
n[0] = n1;
v[1] = v2;
n[1] = n2;
v[2] = v3;
n[2] = n3;
em[0] = em1; /* edge mark of the edge between v1 and v2 */
em[1] = em2; /* edge mark of the edge between v2 and v3 */
em[2] = em3; /* edge mark of the edge between v3 and v1 */
k = 0;
for (i = 0; i < 3; i++) {
j = (i + 1) % 3;
if (clip[i] == NOT_CLIPPED) {
blender::copy_v3_v3(triCoords[k], v[i]);
blender::copy_v3_v3(triNormals[k], n[i]);
edgeMarks[k] = em[i];
k++;
if (clip[j] != NOT_CLIPPED) {
clipLine(v[i], v[j], triCoords[k], (clip[j] == CLIPPED_BY_NEAR) ? _z_near : _z_far);
blender::copy_v3_v3(triNormals[k], n[j]);
edgeMarks[k] = false;
k++;
}
}
else if (clip[i] != clip[j]) {
if (clip[j] == NOT_CLIPPED) {
clipLine(v[i], v[j], triCoords[k], (clip[i] == CLIPPED_BY_NEAR) ? _z_near : _z_far);
blender::copy_v3_v3(triNormals[k], n[i]);
edgeMarks[k] = em[i];
k++;
}
else {
clipLine(v[i], v[j], triCoords[k], (clip[i] == CLIPPED_BY_NEAR) ? _z_near : _z_far);
blender::copy_v3_v3(triNormals[k], n[i]);
edgeMarks[k] = em[i];
k++;
clipLine(v[i], v[j], triCoords[k], (clip[j] == CLIPPED_BY_NEAR) ? _z_near : _z_far);
blender::copy_v3_v3(triNormals[k], n[j]);
edgeMarks[k] = false;
k++;
}
}
}
BLI_assert(k == 2 + numTris);
(void)numTris; /* Ignored in release builds. */
}
void BlenderFileLoader::addTriangle(LoaderState *ls,
float v1[3],
float v2[3],
float v3[3],
float n1[3],
float n2[3],
float n3[3],
bool fm,
bool em1,
bool em2,
bool em3)
{
float *fv[3], *fn[3];
#if 0
float len;
#endif
uint i, j;
IndexedFaceSet::FaceEdgeMark marks = 0;
// initialize the bounding box by the first vertex
if (ls->currentIndex == 0) {
blender::copy_v3_v3(ls->minBBox, v1);
blender::copy_v3_v3(ls->maxBBox, v1);
}
fv[0] = v1;
fn[0] = n1;
fv[1] = v2;
fn[1] = n2;
fv[2] = v3;
fn[2] = n3;
for (i = 0; i < 3; i++) {
blender::copy_v3_v3(ls->pv, fv[i]);
blender::copy_v3_v3(ls->pn, fn[i]);
// update the bounding box
for (j = 0; j < 3; j++) {
if (ls->minBBox[j] > ls->pv[j]) {
ls->minBBox[j] = ls->pv[j];
}
if (ls->maxBBox[j] < ls->pv[j]) {
ls->maxBBox[j] = ls->pv[j];
}
}
#if 0
len = len_v3v3(fv[i], fv[(i + 1) % 3]);
if (_minEdgeSize > len) {
_minEdgeSize = len;
}
#endif
*ls->pvi = ls->currentIndex;
*ls->pni = ls->currentIndex;
*ls->pmi = ls->currentMIndex;
ls->currentIndex += 3;
ls->pv += 3;
ls->pn += 3;
ls->pvi++;
ls->pni++;
ls->pmi++;
}
if (fm) {
marks |= IndexedFaceSet::FACE_MARK;
}
if (em1) {
marks |= IndexedFaceSet::EDGE_MARK_V1V2;
}
if (em2) {
marks |= IndexedFaceSet::EDGE_MARK_V2V3;
}
if (em3) {
marks |= IndexedFaceSet::EDGE_MARK_V3V1;
}
*(ls->pm++) = marks;
}
// With A, B and P indicating the three vertices of a given triangle, returns:
// 1 if points A and B are in the same position in the 3D space;
// 2 if the distance between point P and line segment AB is zero; and
// zero otherwise.
int BlenderFileLoader::testDegenerateTriangle(float v1[3], float v2[3], float v3[3])
{
const float eps = 1.0e-6;
const float eps_sq = eps * eps;
#if 0
float area = area_tri_v3(v1, v2, v3);
bool verbose = (area < 1.0e-6);
#endif
if (blender::equals_v3v3(v1, v2) || blender::equals_v3v3(v2, v3) || blender::equals_v3v3(v1, v3))
{
#if 0
if (verbose && G.debug & blender::G_DEBUG_FREESTYLE) {
printf("BlenderFileLoader::testDegenerateTriangle = 1\n");
}
#endif
return 1;
}
if (blender::dist_squared_to_line_segment_v3(v1, v2, v3) < eps_sq ||
blender::dist_squared_to_line_segment_v3(v2, v1, v3) < eps_sq ||
blender::dist_squared_to_line_segment_v3(v3, v1, v2) < eps_sq)
{
#if 0
if (verbose && G.debug & blender::G_DEBUG_FREESTYLE) {
printf("BlenderFileLoader::testDegenerateTriangle = 2\n");
}
#endif
return 2;
}
#if 0
if (verbose && G.debug & blender::G_DEBUG_FREESTYLE) {
printf("BlenderFileLoader::testDegenerateTriangle = 0\n");
}
#endif
return 0;
}
static bool testEdgeMark(blender::Mesh *mesh,
const blender::VArray<bool> &fed,
const blender::int3 &tri,
int i)
{
const Span<blender::int2> edges = mesh->edges();
const Span<int> corner_verts = mesh->corner_verts();
const Span<int> corner_edges = mesh->corner_edges();
const int corner = tri[i];
const int corner_next = tri[(i + 1) % 3];
const blender::int2 &edge = edges[corner_edges[corner]];
if (!ELEM(corner_verts[corner_next], edge[0], edge[1])) {
/* Not an edge in the original mesh before triangulation. */
return false;
}
return fed[corner_edges[corner]];
}
void BlenderFileLoader::insertShapeNode(blender::Object *ob, blender::Mesh *mesh, int id)
{
using namespace blender;
char *name = ob->id.name + 2;
const Span<float3> vert_positions = mesh->vert_positions();
const OffsetIndices mesh_polys = mesh->faces();
const Span<int> corner_verts = mesh->corner_verts();
// Compute loop triangles
int tottri = poly_to_tri_count(mesh->faces_num, mesh->corners_num);
blender::int3 *corner_tris = MEM_new_array_uninitialized<blender::int3>(size_t(tottri),
__func__);
blender::bke::mesh::corner_tris_calc(
vert_positions, mesh_polys, corner_verts, {corner_tris, tottri});
const Span<int> tri_faces = mesh->corner_tri_faces();
const Span<blender::float3> corner_normals = mesh->corner_normals();
const bke::AttributeAccessor attributes = mesh->attributes();
// Get other mesh data
const VArray<bool> fed = *attributes.lookup_or_default<bool>(
"freestyle_edge", bke::AttrDomain::Edge, false);
const VArray<bool> ffa = *attributes.lookup_or_default<bool>(
"freestyle_face", bke::AttrDomain::Face, false);
// Compute view matrix
Object *ob_camera_eval = DEG_get_evaluated(_depsgraph, RE_GetCamera(_re));
float viewinv[4][4], viewmat[4][4];
RE_GetCameraModelMatrix(_re, ob_camera_eval, viewinv);
invert_m4_m4(viewmat, viewinv);
// Compute matrix including camera transform
float obmat[4][4], nmat[4][4];
mul_m4_m4m4(obmat, viewmat, ob->object_to_world().ptr());
invert_m4_m4(nmat, obmat);
transpose_m4(nmat);
// We count the number of triangles after the clipping by the near and far view
// planes is applied (NOTE: mesh vertices are in the camera coordinate system).
uint numFaces = 0;
float v1[3], v2[3], v3[3];
float n1[3], n2[3], n3[3], facenormal[3];
int clip[3];
for (int a = 0; a < tottri; a++) {
const int3 &tri = corner_tris[a];
copy_v3_v3(v1, vert_positions[corner_verts[tri[0]]]);
copy_v3_v3(v2, vert_positions[corner_verts[tri[1]]]);
copy_v3_v3(v3, vert_positions[corner_verts[tri[2]]]);
mul_m4_v3(obmat, v1);
mul_m4_v3(obmat, v2);
mul_m4_v3(obmat, v3);
v1[2] += _z_offset;
v2[2] += _z_offset;
v3[2] += _z_offset;
numFaces += countClippedFaces(v1, v2, v3, clip);
}
#if 0
if (blender::G.debug & blender::G_DEBUG_FREESTYLE) {
cout << "numFaces " << numFaces << endl;
}
#endif
if (numFaces == 0) {
MEM_delete(corner_tris);
return;
}
// We allocate memory for the meshes to be imported
NodeGroup *currentMesh = new NodeGroup;
NodeShape *shape = new NodeShape;
uint vSize = 3 * 3 * numFaces;
float *vertices = new float[vSize];
uint nSize = vSize;
float *normals = new float[nSize];
uint *numVertexPerFaces = new uint[numFaces];
vector<Material *> meshMaterials;
vector<FrsMaterial> meshFrsMaterials;
IndexedFaceSet::TRIANGLES_STYLE *faceStyle = new IndexedFaceSet::TRIANGLES_STYLE[numFaces];
uint i;
for (i = 0; i < numFaces; i++) {
faceStyle[i] = IndexedFaceSet::TRIANGLES;
numVertexPerFaces[i] = 3;
}
IndexedFaceSet::FaceEdgeMark *faceEdgeMarks = new IndexedFaceSet::FaceEdgeMark[numFaces];
uint viSize = 3 * numFaces;
uint *VIndices = new uint[viSize];
uint niSize = viSize;
uint *NIndices = new uint[niSize];
uint *MIndices = new uint[viSize]; // Material Indices
LoaderState ls;
ls.pv = vertices;
ls.pn = normals;
ls.pm = faceEdgeMarks;
ls.pvi = VIndices;
ls.pni = NIndices;
ls.pmi = MIndices;
ls.currentIndex = 0;
ls.currentMIndex = 0;
FrsMaterial tmpMat;
const VArray<int> material_indices = *attributes.lookup_or_default<int>(
"material_index", bke::AttrDomain::Face, 0);
const VArray<bool> sharp_faces = *attributes.lookup_or_default<bool>(
"sharp_face", bke::AttrDomain::Face, false);
// We parse the vlak nodes again and import meshes while applying the clipping
// by the near and far view planes.
for (int a = 0; a < tottri; a++) {
const int3 &tri = corner_tris[a];
const int poly_i = tri_faces[a];
Material *mat = BKE_object_material_get(ob, material_indices[poly_i] + 1);
copy_v3_v3(v1, vert_positions[corner_verts[tri[0]]]);
copy_v3_v3(v2, vert_positions[corner_verts[tri[1]]]);
copy_v3_v3(v3, vert_positions[corner_verts[tri[2]]]);
mul_m4_v3(obmat, v1);
mul_m4_v3(obmat, v2);
mul_m4_v3(obmat, v3);
v1[2] += _z_offset;
v2[2] += _z_offset;
v3[2] += _z_offset;
if (_smooth && (!sharp_faces[poly_i])) {
copy_v3_v3(n1, corner_normals[tri[0]]);
copy_v3_v3(n2, corner_normals[tri[1]]);
copy_v3_v3(n3, corner_normals[tri[2]]);
mul_mat3_m4_v3(nmat, n1);
mul_mat3_m4_v3(nmat, n2);
mul_mat3_m4_v3(nmat, n3);
normalize_v3(n1);
normalize_v3(n2);
normalize_v3(n3);
}
else {
normal_tri_v3(facenormal, v3, v2, v1);
copy_v3_v3(n1, facenormal);
copy_v3_v3(n2, facenormal);
copy_v3_v3(n3, facenormal);
}
uint numTris = countClippedFaces(v1, v2, v3, clip);
if (numTris == 0) {
continue;
}
bool fm = ffa[poly_i];
bool em1 = false, em2 = false, em3 = false;
if (fed) {
em1 = testEdgeMark(mesh, fed, tri, 0);
em2 = testEdgeMark(mesh, fed, tri, 1);
em3 = testEdgeMark(mesh, fed, tri, 2);
}
if (mat) {
tmpMat.setLine(mat->line_col[0], mat->line_col[1], mat->line_col[2], mat->line_col[3]);
tmpMat.setDiffuse(mat->r, mat->g, mat->b, 1.0f);
tmpMat.setSpecular(mat->specr, mat->specg, mat->specb, 1.0f);
tmpMat.setShininess(128.0f);
tmpMat.setPriority(mat->line_priority);
}
if (meshMaterials.empty()) {
meshMaterials.push_back(mat);
meshFrsMaterials.push_back(tmpMat);
shape->setFrsMaterial(tmpMat);
}
else {
// find if the Blender material is already in the list
uint i = 0;
bool found = false;
for (vector<Material *>::iterator it = meshMaterials.begin(), itend = meshMaterials.end();
it != itend;
it++, i++)
{
if (*it == mat) {
ls.currentMIndex = i;
found = true;
break;
}
}
if (!found) {
meshMaterials.push_back(mat);
meshFrsMaterials.push_back(tmpMat);
ls.currentMIndex = meshFrsMaterials.size() - 1;
}
}
float triCoords[5][3], triNormals[5][3];
bool edgeMarks[5]; // edgeMarks[i] is for the edge between i-th and (i+1)-th vertices
clipTriangle(
numTris, triCoords, v1, v2, v3, triNormals, n1, n2, n3, edgeMarks, em1, em2, em3, clip);
for (i = 0; i < numTris; i++) {
addTriangle(&ls,
triCoords[0],
triCoords[i + 1],
triCoords[i + 2],
triNormals[0],
triNormals[i + 1],
triNormals[i + 2],
fm,
(i == 0) ? edgeMarks[0] : false,
edgeMarks[i + 1],
(i == numTris - 1) ? edgeMarks[i + 2] : false);
_numFacesRead++;
}
}
MEM_delete(corner_tris);
// We might have several times the same vertex. We want a clean
// shape with no real-vertex. Here, we are making a cleaning pass.
float *cleanVertices = nullptr;
uint cvSize;
uint *cleanVIndices = nullptr;
GeomCleaner::CleanIndexedVertexArray(
vertices, vSize, VIndices, viSize, &cleanVertices, &cvSize, &cleanVIndices);
float *cleanNormals = nullptr;
uint cnSize;
uint *cleanNIndices = nullptr;
GeomCleaner::CleanIndexedVertexArray(
normals, nSize, NIndices, niSize, &cleanNormals, &cnSize, &cleanNIndices);
// format materials array
FrsMaterial **marray = new FrsMaterial *[meshFrsMaterials.size()];
uint mindex = 0;
for (vector<FrsMaterial>::iterator m = meshFrsMaterials.begin(), mend = meshFrsMaterials.end();
m != mend;
++m)
{
marray[mindex] = new FrsMaterial(*m);
++mindex;
}
// deallocates memory:
delete[] vertices;
delete[] normals;
delete[] VIndices;
delete[] NIndices;
// Fix for degenerated triangles
// A degenerate triangle is a triangle such that
// 1) A and B are in the same position in the 3D space; or
// 2) the distance between point P and line segment AB is zero.
// Only those degenerate triangles in the second form are resolved here
// by adding a small offset to P, whereas those in the first form are
// addressed later in WShape::MakeFace().
vector<detri_t> detriList;
Vec3r zero(0.0, 0.0, 0.0);
uint vi0, vi1, vi2;
for (i = 0; i < viSize; i += 3) {
detri_t detri;
vi0 = cleanVIndices[i];
vi1 = cleanVIndices[i + 1];
vi2 = cleanVIndices[i + 2];
Vec3r v0(cleanVertices[vi0], cleanVertices[vi0 + 1], cleanVertices[vi0 + 2]);
Vec3r v1(cleanVertices[vi1], cleanVertices[vi1 + 1], cleanVertices[vi1 + 2]);
Vec3r v2(cleanVertices[vi2], cleanVertices[vi2 + 1], cleanVertices[vi2 + 2]);
if (v0 == v1 || v0 == v2 || v1 == v2) {
continue; // do nothing for now
}
if (GeomUtils::distPointSegment<Vec3r>(v0, v1, v2) < 1.0e-6) {
detri.viP = vi0;
detri.viA = vi1;
detri.viB = vi2;
}
else if (GeomUtils::distPointSegment<Vec3r>(v1, v0, v2) < 1.0e-6) {
detri.viP = vi1;
detri.viA = vi0;
detri.viB = vi2;
}
else if (GeomUtils::distPointSegment<Vec3r>(v2, v0, v1) < 1.0e-6) {
detri.viP = vi2;
detri.viA = vi0;
detri.viB = vi1;
}
else {
continue;
}
detri.v = zero;
detri.n = 0;
for (uint j = 0; j < viSize; j += 3) {
if (i == j) {
continue;
}
vi0 = cleanVIndices[j];
vi1 = cleanVIndices[j + 1];
vi2 = cleanVIndices[j + 2];
Vec3r v0(cleanVertices[vi0], cleanVertices[vi0 + 1], cleanVertices[vi0 + 2]);
Vec3r v1(cleanVertices[vi1], cleanVertices[vi1 + 1], cleanVertices[vi1 + 2]);
Vec3r v2(cleanVertices[vi2], cleanVertices[vi2 + 1], cleanVertices[vi2 + 2]);
if (detri.viP == vi0 && (detri.viA == vi1 || detri.viB == vi1)) {
detri.v += (v2 - v0);
detri.n++;
}
else if (detri.viP == vi0 && (detri.viA == vi2 || detri.viB == vi2)) {
detri.v += (v1 - v0);
detri.n++;
}
else if (detri.viP == vi1 && (detri.viA == vi0 || detri.viB == vi0)) {
detri.v += (v2 - v1);
detri.n++;
}
else if (detri.viP == vi1 && (detri.viA == vi2 || detri.viB == vi2)) {
detri.v += (v0 - v1);
detri.n++;
}
else if (detri.viP == vi2 && (detri.viA == vi0 || detri.viB == vi0)) {
detri.v += (v1 - v2);
detri.n++;
}
else if (detri.viP == vi2 && (detri.viA == vi1 || detri.viB == vi1)) {
detri.v += (v0 - v2);
detri.n++;
}
}
if (detri.n > 0) {
detri.v.normalizeSafe();
}
detriList.push_back(detri);
}
if (!detriList.empty()) {
vector<detri_t>::iterator v;
for (v = detriList.begin(); v != detriList.end(); v++) {
detri_t detri = (*v);
if (detri.n == 0) {
cleanVertices[detri.viP] = cleanVertices[detri.viA];
cleanVertices[detri.viP + 1] = cleanVertices[detri.viA + 1];
cleanVertices[detri.viP + 2] = cleanVertices[detri.viA + 2];
}
else if (detri.v.norm() > 0.0) {
cleanVertices[detri.viP] += 1.0e-5 * detri.v.x();
cleanVertices[detri.viP + 1] += 1.0e-5 * detri.v.y();
cleanVertices[detri.viP + 2] += 1.0e-5 * detri.v.z();
}
}
if (blender::G.debug & blender::G_DEBUG_FREESTYLE) {
printf("Warning: Object %s contains %lu degenerated triangle%s (strokes may be incorrect)\n",
name,
ulong(detriList.size()),
(detriList.size() > 1) ? "s" : "");
}
}
// Create the IndexedFaceSet with the retrieved attributes
IndexedFaceSet *rep;
rep = new IndexedFaceSet(cleanVertices,
cvSize,
cleanNormals,
cnSize,
marray,
meshFrsMaterials.size(),
nullptr,
0,
numFaces,
numVertexPerFaces,
faceStyle,
faceEdgeMarks,
cleanVIndices,
viSize,
cleanNIndices,
niSize,
MIndices,
viSize,
nullptr,
0,
0);
// sets the id of the rep
rep->setId(Id(id, 0));
rep->setName(ob->id.name + 2);
rep->setLibraryPath(ob->id.lib ? ob->id.lib->filepath : "");
const BBox<Vec3r> bbox = BBox<Vec3r>(Vec3r(ls.minBBox[0], ls.minBBox[1], ls.minBBox[2]),
Vec3r(ls.maxBBox[0], ls.maxBBox[1], ls.maxBBox[2]));
rep->setBBox(bbox);
shape->AddRep(rep);
currentMesh->AddChild(shape);
_Scene->AddChild(currentMesh);
}
} /* namespace Freestyle */

View File

@@ -0,0 +1,148 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
*/
#include <float.h>
#include <string.h>
#include "../geometry/BBox.h"
#include "../geometry/Geom.h"
#include "../geometry/GeomCleaner.h"
#include "../geometry/GeomUtils.h"
#include "../scene_graph/IndexedFaceSet.h"
#include "../scene_graph/NodeGroup.h"
#include "../scene_graph/NodeShape.h"
#include "../scene_graph/NodeTransform.h"
#include "../system/FreestyleConfig.h"
#include "../system/RenderMonitor.h"
#include "MEM_guardedalloc.h"
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "render_types.h"
#include "BKE_lib_id.hh"
#include "BKE_material.hh"
#include "BKE_mesh.h"
#include "BKE_scene.hh"
#include "BLI_iterator.h"
#include "BLI_listbase.h"
#include "DEG_depsgraph_query.hh"
#include "MEM_guardedalloc.h"
namespace Freestyle {
class NodeGroup;
struct LoaderState {
float *pv;
float *pn;
IndexedFaceSet::FaceEdgeMark *pm;
uint *pvi;
uint *pni;
uint *pmi;
uint currentIndex;
uint currentMIndex;
float minBBox[3];
float maxBBox[3];
};
class BlenderFileLoader {
public:
/** Builds a MaxFileLoader */
BlenderFileLoader(blender::Render *re,
blender::ViewLayer *view_layer,
blender::Depsgraph *depsgraph);
virtual ~BlenderFileLoader();
/** Loads the 3D scene and returns a pointer to the scene root node */
NodeGroup *Load();
/** Gets the number of read faces */
inline uint numFacesRead()
{
return _numFacesRead;
}
#if 0
/** Gets the smallest edge size read */
inline real minEdgeSize()
{
return _minEdgeSize;
}
#endif
/** Modifiers */
inline void setRenderMonitor(RenderMonitor *iRenderMonitor)
{
_pRenderMonitor = iRenderMonitor;
}
protected:
void insertShapeNode(blender::Object *ob, blender::Mesh *mesh, int id);
int testDegenerateTriangle(float v1[3], float v2[3], float v3[3]);
int countClippedFaces(float v1[3], float v2[3], float v3[3], int clip[3]);
void clipLine(float v1[3], float v2[3], float c[3], float z);
void clipTriangle(int numTris,
float triCoords[][3],
float v1[3],
float v2[3],
float v3[3],
float triNormals[][3],
float n1[3],
float n2[3],
float n3[3],
bool edgeMarks[5],
bool em1,
bool em2,
bool em3,
const int clip[3]);
void addTriangle(struct LoaderState *ls,
float v1[3],
float v2[3],
float v3[3],
float n1[3],
float n2[3],
float n3[3],
bool fm,
bool em1,
bool em2,
bool em3);
protected:
struct detri_t {
uint viA, viB, viP; // 0 <= viA, viB, viP < viSize
Vec3r v;
uint n;
};
blender::Render *_re;
blender::Depsgraph *_depsgraph;
NodeGroup *_Scene;
uint _numFacesRead;
#if 0
real _minEdgeSize;
#endif
bool _smooth; /* if true, face smoothness is taken into account */
float _z_near, _z_far;
float _z_offset;
RenderMonitor *_pRenderMonitor;
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:BlenderFileLoader")
};
} /* namespace Freestyle */

View File

@@ -0,0 +1,908 @@
/* SPDX-FileCopyrightText: 2008-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BlenderStrokeRenderer.h"
#include "../application/AppConfig.h"
#include "../stroke/Canvas.h"
#include "MEM_guardedalloc.h"
#include "RNA_access.hh"
#include "RNA_prototypes.hh"
#include "RNA_types.hh"
#include "DNA_camera_types.h"
#include "DNA_collection_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_listBase.h"
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "BKE_attribute.h"
#include "BKE_attribute.hh"
#include "BKE_collection.hh"
#include "BKE_customdata.hh"
#include "BKE_global.hh"
#include "BKE_idprop.hh"
#include "BKE_layer.hh"
#include "BKE_lib_id.hh" /* free_libblock */
#include "BKE_main.hh"
#include "BKE_material.hh"
#include "BKE_mesh.hh"
#include "BKE_node.hh"
#include "BKE_node_legacy_types.hh"
#include "BKE_node_tree_update.hh"
#include "BKE_object.hh"
#include "BKE_scene.hh"
#include "BLI_listbase.h"
#include "BLI_math_color.h"
#include "BLI_math_vector.h"
#include "BLI_math_vector_types.hh"
#include "BLI_utildefines.h"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_build.hh"
#include "RE_pipeline.h"
#include "render_types.h"
#include <climits>
using blender::float3;
using blender::Material;
namespace Freestyle {
const char *BlenderStrokeRenderer::uvNames[] = {"along_stroke", "along_stroke_tips"};
BlenderStrokeRenderer::BlenderStrokeRenderer(blender::Render *re, int render_count)
{
using namespace blender;
freestyle_bmain = blender::BKE_main_new();
/* NOTE(@sergey): We use the same window manager for freestyle `bmain` as real `bmain` uses.
* This is needed because freestyle's `bmain` could be used to tag scenes for update,
* which implies call of #ED_render_scene_update in some cases and that function
* requires proper window manager to present. */
freestyle_bmain->wm = re->main->wm;
// for stroke mesh generation
_width = re->winx;
_height = re->winy;
old_scene = re->scene;
char name[MAX_ID_NAME - 2];
SNPRINTF(name, "FRS%d_%s", render_count, re->scene->id.name + 2);
freestyle_scene = BKE_scene_add(freestyle_bmain, name);
freestyle_scene->r.cfra = old_scene->r.cfra;
freestyle_scene->r.mode = old_scene->r.mode & ~(R_EDGE_FRS | R_BORDER);
freestyle_scene->r.xsch = re->rectx; // old_scene->r.xsch
freestyle_scene->r.ysch = re->recty; // old_scene->r.ysch
freestyle_scene->r.xasp = 1.0f; // old_scene->r.xasp;
freestyle_scene->r.yasp = 1.0f; // old_scene->r.yasp;
freestyle_scene->r.size = 100; // old_scene->r.size
freestyle_scene->r.color_mgt_flag = 0; // old_scene->r.color_mgt_flag;
freestyle_scene->r.scemode = (old_scene->r.scemode &
~(R_SINGLE_LAYER | R_NO_FRAME_UPDATE | R_MULTIVIEW)) &
(re->r.scemode);
freestyle_scene->r.flag = old_scene->r.flag;
freestyle_scene->r.threads = old_scene->r.threads;
freestyle_scene->r.border.xmin = old_scene->r.border.xmin;
freestyle_scene->r.border.ymin = old_scene->r.border.ymin;
freestyle_scene->r.border.xmax = old_scene->r.border.xmax;
freestyle_scene->r.border.ymax = old_scene->r.border.ymax;
STRNCPY(freestyle_scene->r.pic, old_scene->r.pic);
freestyle_scene->r.dither_intensity = old_scene->r.dither_intensity;
STRNCPY(freestyle_scene->r.engine, old_scene->r.engine);
if (blender::G.debug & blender::G_DEBUG_FREESTYLE) {
cout << "Stroke rendering engine : " << freestyle_scene->r.engine << endl;
}
freestyle_scene->r.im_format.color_mode = ImColorMode::RGBA;
freestyle_scene->r.im_format.imtype = R_IMF_IMTYPE_PNG;
// Copy ID properties, including Cycles render properties
if (old_scene->id.properties) {
freestyle_scene->id.properties = IDP_CopyProperty_ex(old_scene->id.properties, 0);
}
if (old_scene->id.system_properties) {
freestyle_scene->id.system_properties = IDP_CopyProperty_ex(old_scene->id.system_properties,
0);
}
// Copy eevee render settings.
BKE_scene_copy_data_eevee(freestyle_scene, old_scene);
/* Render with transparent background. */
freestyle_scene->r.alphamode = R_ALPHAPREMUL;
if (blender::G.debug & blender::G_DEBUG_FREESTYLE) {
printf("%s: %d thread(s)\n", __func__, BKE_render_num_threads(&freestyle_scene->r));
}
BKE_scene_set_background(freestyle_bmain, freestyle_scene);
// Scene layer.
ViewLayer *view_layer = (ViewLayer *)freestyle_scene->view_layers.first;
view_layer->layflag = SCE_LAY_SOLID;
// Camera
Object *object_camera = BKE_object_add(
freestyle_bmain, freestyle_scene, view_layer, OB_CAMERA, nullptr);
Camera *camera = (Camera *)object_camera->data;
camera->type = CAM_ORTHO;
camera->ortho_scale = max(re->rectx, re->recty);
camera->clip_start = 0.1f;
camera->clip_end = 100.0f;
_z_delta = 0.00001f;
_z = camera->clip_start + _z_delta;
object_camera->loc[0] = re->disprect.xmin + 0.5f * re->rectx;
object_camera->loc[1] = re->disprect.ymin + 0.5f * re->recty;
object_camera->loc[2] = 1.0f;
freestyle_scene->camera = object_camera;
// Reset serial mesh ID (used for BlenderStrokeRenderer::NewMesh())
_mesh_id = 0xffffffff;
// Depsgraph
freestyle_depsgraph = DEG_graph_new(
freestyle_bmain, freestyle_scene, view_layer, DAG_EVAL_RENDER);
DEG_graph_id_tag_update(freestyle_bmain, freestyle_depsgraph, &freestyle_scene->id, 0);
DEG_graph_id_tag_update(freestyle_bmain, freestyle_depsgraph, &object_camera->id, 0);
DEG_graph_tag_relations_update(freestyle_depsgraph);
}
BlenderStrokeRenderer::~BlenderStrokeRenderer()
{
DEG_graph_free(freestyle_depsgraph);
FreeStrokeGroups();
/* detach the window manager from freestyle bmain (see comments
* in add_freestyle() for more detail)
*/
freestyle_bmain->wm.clear_no_delete();
BKE_main_free(freestyle_bmain);
}
float BlenderStrokeRenderer::get_stroke_vertex_z() const
{
float z = _z;
BlenderStrokeRenderer *self = const_cast<BlenderStrokeRenderer *>(this);
self->_z += _z_delta;
return -z;
}
uint BlenderStrokeRenderer::get_stroke_mesh_id() const
{
uint mesh_id = _mesh_id;
BlenderStrokeRenderer *self = const_cast<BlenderStrokeRenderer *>(this);
self->_mesh_id--;
return mesh_id;
}
Material *BlenderStrokeRenderer::GetStrokeShader(blender::Main *bmain,
blender::bNodeTree *iNodeTree,
bool do_id_user)
{
using namespace blender;
Material *ma = BKE_material_add(bmain, "stroke_shader");
bNodeTree *ntree;
bNode *output_linestyle = nullptr;
bNodeSocket *fromsock, *tosock;
PointerRNA fromptr, toptr;
NodeShaderAttribute *storage;
id_us_min(&ma->id);
if (iNodeTree) {
// make a copy of linestyle->nodetree
if (ma->nodetree) {
bke::node_tree_free_embedded_tree(ma->nodetree);
MEM_delete(ma->nodetree);
ma->nodetree = nullptr;
}
ntree = blender::bke::node_tree_copy_tree_ex(*iNodeTree, bmain, do_id_user);
// find the active Output Line Style node
for (bNode &node : ntree->nodes) {
if (node.type_legacy == SH_NODE_OUTPUT_LINESTYLE && (node.flag & NODE_DO_OUTPUT)) {
output_linestyle = &node;
break;
}
}
ma->nodetree = ntree;
ntree->owner_id = &ma->id;
}
else {
ntree = ma->nodetree;
}
ma->blend_method = MA_BM_HASHED;
bNode *input_attr_color = blender::bke::node_add_static_node(nullptr, *ntree, SH_NODE_ATTRIBUTE);
input_attr_color->location[0] = 0.0f;
input_attr_color->location[1] = -200.0f;
storage = (NodeShaderAttribute *)input_attr_color->storage;
STRNCPY(storage->name, "Color");
bNode *mix_rgb_color = blender::bke::node_add_static_node(
nullptr, *ntree, SH_NODE_MIX_RGB_LEGACY);
mix_rgb_color->custom1 = MA_RAMP_BLEND; // Mix
mix_rgb_color->location[0] = 200.0f;
mix_rgb_color->location[1] = -200.0f;
tosock = (bNodeSocket *)BLI_findlink(&mix_rgb_color->inputs, 0); // Fac
toptr = RNA_pointer_create_discrete((ID *)ntree, RNA_NodeSocket, tosock);
RNA_float_set(&toptr, "default_value", 0.0f);
bNode *input_attr_alpha = blender::bke::node_add_static_node(nullptr, *ntree, SH_NODE_ATTRIBUTE);
input_attr_alpha->location[0] = 400.0f;
input_attr_alpha->location[1] = 300.0f;
storage = (NodeShaderAttribute *)input_attr_alpha->storage;
STRNCPY(storage->name, "Alpha");
bNode *mix_rgb_alpha = blender::bke::node_add_static_node(
nullptr, *ntree, SH_NODE_MIX_RGB_LEGACY);
mix_rgb_alpha->custom1 = MA_RAMP_BLEND; // Mix
mix_rgb_alpha->location[0] = 600.0f;
mix_rgb_alpha->location[1] = 300.0f;
tosock = (bNodeSocket *)BLI_findlink(&mix_rgb_alpha->inputs, 0); // Fac
toptr = RNA_pointer_create_discrete((ID *)ntree, RNA_NodeSocket, tosock);
RNA_float_set(&toptr, "default_value", 0.0f);
bNode *shader_emission = blender::bke::node_add_static_node(nullptr, *ntree, SH_NODE_EMISSION);
shader_emission->location[0] = 400.0f;
shader_emission->location[1] = -200.0f;
bNode *input_light_path = blender::bke::node_add_static_node(
nullptr, *ntree, SH_NODE_LIGHT_PATH);
input_light_path->location[0] = 400.0f;
input_light_path->location[1] = 100.0f;
bNode *mix_shader_color = blender::bke::node_add_static_node(
nullptr, *ntree, SH_NODE_MIX_SHADER);
mix_shader_color->location[0] = 600.0f;
mix_shader_color->location[1] = -100.0f;
bNode *shader_transparent = blender::bke::node_add_static_node(
nullptr, *ntree, SH_NODE_BSDF_TRANSPARENT);
shader_transparent->location[0] = 600.0f;
shader_transparent->location[1] = 100.0f;
bNode *mix_shader_alpha = blender::bke::node_add_static_node(
nullptr, *ntree, SH_NODE_MIX_SHADER);
mix_shader_alpha->location[0] = 800.0f;
mix_shader_alpha->location[1] = 100.0f;
bNode *output_material = blender::bke::node_add_static_node(
nullptr, *ntree, SH_NODE_OUTPUT_MATERIAL);
output_material->location[0] = 1000.0f;
output_material->location[1] = 100.0f;
fromsock = (bNodeSocket *)BLI_findlink(&input_attr_color->outputs, 0); // Color
tosock = (bNodeSocket *)BLI_findlink(&mix_rgb_color->inputs, 1); // Color1
blender::bke::node_add_link(*ntree, *input_attr_color, *fromsock, *mix_rgb_color, *tosock);
fromsock = (bNodeSocket *)BLI_findlink(&mix_rgb_color->outputs, 0); // Color
tosock = (bNodeSocket *)BLI_findlink(&shader_emission->inputs, 0); // Color
blender::bke::node_add_link(*ntree, *mix_rgb_color, *fromsock, *shader_emission, *tosock);
fromsock = (bNodeSocket *)BLI_findlink(&shader_emission->outputs, 0); // Emission
tosock = (bNodeSocket *)BLI_findlink(&mix_shader_color->inputs, 2); // Shader (second)
blender::bke::node_add_link(*ntree, *shader_emission, *fromsock, *mix_shader_color, *tosock);
fromsock = (bNodeSocket *)BLI_findlink(&input_light_path->outputs, 0); // In Camera Ray
tosock = (bNodeSocket *)BLI_findlink(&mix_shader_color->inputs, 0); // Fac
blender::bke::node_add_link(*ntree, *input_light_path, *fromsock, *mix_shader_color, *tosock);
fromsock = (bNodeSocket *)BLI_findlink(&mix_rgb_alpha->outputs, 0); // Color
tosock = (bNodeSocket *)BLI_findlink(&mix_shader_alpha->inputs, 0); // Fac
blender::bke::node_add_link(*ntree, *mix_rgb_alpha, *fromsock, *mix_shader_alpha, *tosock);
fromsock = (bNodeSocket *)BLI_findlink(&input_attr_alpha->outputs, 0); // Color
tosock = (bNodeSocket *)BLI_findlink(&mix_rgb_alpha->inputs, 1); // Color1
blender::bke::node_add_link(*ntree, *input_attr_alpha, *fromsock, *mix_rgb_alpha, *tosock);
fromsock = (bNodeSocket *)BLI_findlink(&shader_transparent->outputs, 0); // BSDF
tosock = (bNodeSocket *)BLI_findlink(&mix_shader_alpha->inputs, 1); // Shader (first)
blender::bke::node_add_link(*ntree, *shader_transparent, *fromsock, *mix_shader_alpha, *tosock);
fromsock = (bNodeSocket *)BLI_findlink(&mix_shader_color->outputs, 0); // Shader
tosock = (bNodeSocket *)BLI_findlink(&mix_shader_alpha->inputs, 2); // Shader (second)
blender::bke::node_add_link(*ntree, *mix_shader_color, *fromsock, *mix_shader_alpha, *tosock);
fromsock = (bNodeSocket *)BLI_findlink(&mix_shader_alpha->outputs, 0); // Shader
tosock = (bNodeSocket *)BLI_findlink(&output_material->inputs, 0); // Surface
blender::bke::node_add_link(*ntree, *mix_shader_alpha, *fromsock, *output_material, *tosock);
if (output_linestyle) {
bNodeSocket *outsock;
bNodeLink *link;
mix_rgb_color->custom1 = output_linestyle->custom1; // blend_type
mix_rgb_color->custom2 = output_linestyle->custom2; // use_clamp
outsock = (bNodeSocket *)BLI_findlink(&output_linestyle->inputs, 0); // Color
tosock = (bNodeSocket *)BLI_findlink(&mix_rgb_color->inputs, 2); // Color2
link = (bNodeLink *)BLI_findptr(&ntree->links, outsock, offsetof(bNodeLink, tosock));
if (link) {
blender::bke::node_add_link(
*ntree, *link->fromnode, *link->fromsock, *mix_rgb_color, *tosock);
}
else {
float color[4];
fromptr = RNA_pointer_create_discrete((ID *)ntree, RNA_NodeSocket, outsock);
toptr = RNA_pointer_create_discrete((ID *)ntree, RNA_NodeSocket, tosock);
RNA_float_get_array(&fromptr, "default_value", color);
RNA_float_set_array(&toptr, "default_value", color);
}
outsock = (bNodeSocket *)BLI_findlink(&output_linestyle->inputs, 1); // Color Fac
tosock = (bNodeSocket *)BLI_findlink(&mix_rgb_color->inputs, 0); // Fac
link = (bNodeLink *)BLI_findptr(&ntree->links, outsock, offsetof(bNodeLink, tosock));
if (link) {
blender::bke::node_add_link(
*ntree, *link->fromnode, *link->fromsock, *mix_rgb_color, *tosock);
}
else {
fromptr = RNA_pointer_create_discrete((ID *)ntree, RNA_NodeSocket, outsock);
toptr = RNA_pointer_create_discrete((ID *)ntree, RNA_NodeSocket, tosock);
RNA_float_set(&toptr, "default_value", RNA_float_get(&fromptr, "default_value"));
}
outsock = (bNodeSocket *)BLI_findlink(&output_linestyle->inputs, 2); // Alpha
tosock = (bNodeSocket *)BLI_findlink(&mix_rgb_alpha->inputs, 2); // Color2
link = (bNodeLink *)BLI_findptr(&ntree->links, outsock, offsetof(bNodeLink, tosock));
if (link) {
blender::bke::node_add_link(
*ntree, *link->fromnode, *link->fromsock, *mix_rgb_alpha, *tosock);
}
else {
float color[4];
fromptr = RNA_pointer_create_discrete((ID *)ntree, RNA_NodeSocket, outsock);
toptr = RNA_pointer_create_discrete((ID *)ntree, RNA_NodeSocket, tosock);
color[0] = color[1] = color[2] = RNA_float_get(&fromptr, "default_value");
color[3] = 1.0f;
RNA_float_set_array(&toptr, "default_value", color);
}
outsock = (bNodeSocket *)BLI_findlink(&output_linestyle->inputs, 3); // Alpha Fac
tosock = (bNodeSocket *)BLI_findlink(&mix_rgb_alpha->inputs, 0); // Fac
link = (bNodeLink *)BLI_findptr(&ntree->links, outsock, offsetof(bNodeLink, tosock));
if (link) {
blender::bke::node_add_link(
*ntree, *link->fromnode, *link->fromsock, *mix_rgb_alpha, *tosock);
}
else {
fromptr = RNA_pointer_create_discrete((ID *)ntree, RNA_NodeSocket, outsock);
toptr = RNA_pointer_create_discrete((ID *)ntree, RNA_NodeSocket, tosock);
RNA_float_set(&toptr, "default_value", RNA_float_get(&fromptr, "default_value"));
}
for (bNode &node : ntree->nodes) {
if (node.type_legacy == SH_NODE_UVALONGSTROKE) {
// UV output of the UV Along Stroke node
bNodeSocket *sock = (bNodeSocket *)BLI_findlink(&node.outputs, 0);
// add new UV Map node
bNode *input_uvmap = blender::bke::node_add_static_node(nullptr, *ntree, SH_NODE_UVMAP);
input_uvmap->location[0] = node.location[0] - 200.0f;
input_uvmap->location[1] = node.location[1];
NodeShaderUVMap *storage = (NodeShaderUVMap *)input_uvmap->storage;
if (node.custom1 & 1) { // use_tips
STRNCPY(storage->uv_map, uvNames[1]);
}
else {
STRNCPY(storage->uv_map, uvNames[0]);
}
fromsock = (bNodeSocket *)BLI_findlink(&input_uvmap->outputs, 0); // UV
// replace links from the UV Along Stroke node by links from the UV Map node
for (bNodeLink &link : ntree->links) {
if (link.fromnode == &node && link.fromsock == sock) {
blender::bke::node_add_link(
*ntree, *input_uvmap, *fromsock, *link.tonode, *link.tosock);
}
}
blender::bke::node_remove_socket_links(*ntree, *sock);
}
}
}
blender::bke::node_set_active(*ntree, *output_material);
BKE_ntree_update_after_single_tree_change(*bmain, *ntree);
return ma;
}
void BlenderStrokeRenderer::RenderStrokeRep(StrokeRep *iStrokeRep) const
{
RenderStrokeRepBasic(iStrokeRep);
}
void BlenderStrokeRenderer::RenderStrokeRepBasic(StrokeRep *iStrokeRep) const
{
using namespace blender;
bNodeTree *nt = iStrokeRep->getNodeTree();
Material *ma = _nodetree_hash.lookup_or_add_cb(
nt, [&]() { return BlenderStrokeRenderer::GetStrokeShader(freestyle_bmain, nt, false); });
iStrokeRep->setMaterial(ma);
const vector<Strip *> &strips = iStrokeRep->getStrips();
const bool hasTex = iStrokeRep->hasTex();
int totvert = 0, totedge = 0, faces_num = 0, totloop = 0;
int visible_faces, visible_segments;
for (vector<Strip *>::const_iterator s = strips.begin(), send = strips.end(); s != send; ++s) {
Strip::vertex_container &strip_vertices = (*s)->vertices();
// count visible faces and strip segments
test_strip_visibility(strip_vertices, &visible_faces, &visible_segments);
if (visible_faces == 0) {
continue;
}
totvert += visible_faces + visible_segments * 2;
totedge += visible_faces * 2 + visible_segments;
faces_num += visible_faces;
totloop += visible_faces * 3;
}
BlenderStrokeRenderer *self = const_cast<BlenderStrokeRenderer *>(this); // FIXME
vector<StrokeGroup *> *groups = hasTex ? &self->texturedStrokeGroups : &self->strokeGroups;
StrokeGroup *group;
if (groups->empty() || !(groups->back()->totvert + totvert < MESH_MAX_VERTS &&
groups->back()->materials.size() + 1 < MAXMAT))
{
group = new StrokeGroup;
groups->push_back(group);
}
else {
group = groups->back();
}
group->strokes.push_back(iStrokeRep);
group->totvert += totvert;
group->totedge += totedge;
group->faces_num += faces_num;
group->totloop += totloop;
if (!group->materials.contains(ma)) {
group->materials.add_new(ma, group->materials.size());
}
}
// Check if the triangle is visible (i.e., within the render image boundary)
bool BlenderStrokeRenderer::test_triangle_visibility(StrokeVertexRep *svRep[3]) const
{
int xl, xu, yl, yu;
Vec2r p;
xl = xu = yl = yu = 0;
for (int i = 0; i < 3; i++) {
p = svRep[i]->point2d();
if (p[0] < 0.0) {
xl++;
}
else if (p[0] > _width) {
xu++;
}
if (p[1] < 0.0) {
yl++;
}
else if (p[1] > _height) {
yu++;
}
}
return !(xl == 3 || xu == 3 || yl == 3 || yu == 3);
}
// Check the visibility of faces and strip segments.
void BlenderStrokeRenderer::test_strip_visibility(Strip::vertex_container &strip_vertices,
int *visible_faces,
int *visible_segments) const
{
const int strip_vertex_count = strip_vertices.size();
Strip::vertex_container::iterator v[3];
StrokeVertexRep *svRep[3];
bool visible;
/* Iterate over all vertices and count visible faces and strip segments
* (NOTE: a strip segment is a series of visible faces, while two strip
* segments are separated by one or more invisible faces). */
v[0] = strip_vertices.begin();
v[1] = v[0] + 1;
v[2] = v[0] + 2;
*visible_faces = *visible_segments = 0;
visible = false;
for (int n = 2; n < strip_vertex_count; n++, v[0]++, v[1]++, v[2]++) {
svRep[0] = *(v[0]);
svRep[1] = *(v[1]);
svRep[2] = *(v[2]);
if (test_triangle_visibility(svRep)) {
(*visible_faces)++;
if (!visible) {
(*visible_segments)++;
}
visible = true;
}
else {
visible = false;
}
}
}
// Release allocated memory for stroke groups
void BlenderStrokeRenderer::FreeStrokeGroups()
{
vector<StrokeGroup *>::const_iterator it, itend;
for (it = strokeGroups.begin(), itend = strokeGroups.end(); it != itend; ++it) {
delete (*it);
}
for (it = texturedStrokeGroups.begin(), itend = texturedStrokeGroups.end(); it != itend; ++it) {
delete (*it);
}
}
// Build a scene populated by mesh objects representing stylized strokes
int BlenderStrokeRenderer::GenerateScene()
{
vector<StrokeGroup *>::const_iterator it, itend;
/* Each stroke is placed at a different z depth. Compute the delta based the number
* of vertices to try to maximize the available precision. */
int verts_num = 0;
for (it = strokeGroups.begin(), itend = strokeGroups.end(); it != itend; ++it) {
verts_num += (*it)->totvert;
}
for (it = texturedStrokeGroups.begin(), itend = texturedStrokeGroups.end(); it != itend; ++it) {
verts_num += (*it)->totvert;
}
const blender::Camera *camera = blender::id_cast<const blender::Camera *>(
freestyle_scene->camera->data);
const float z_range = 0.9f;
_z_delta = std::min((verts_num > 0) ? z_range / float(verts_num) : z_range, 1e-5f);
_z = camera->clip_start + _z_delta;
for (it = strokeGroups.begin(), itend = strokeGroups.end(); it != itend; ++it) {
GenerateStrokeMesh(*it, false);
}
for (it = texturedStrokeGroups.begin(), itend = texturedStrokeGroups.end(); it != itend; ++it) {
GenerateStrokeMesh(*it, true);
}
return get_stroke_count();
}
// Return the number of strokes
int BlenderStrokeRenderer::get_stroke_count() const
{
return strokeGroups.size() + texturedStrokeGroups.size();
}
// Build a mesh object representing a group of stylized strokes
void BlenderStrokeRenderer::GenerateStrokeMesh(StrokeGroup *group, bool hasTex)
{
using namespace blender;
#if 0
Object *object_mesh = BKE_object_add(
freestyle_bmain, (ViewLayer *)freestyle_scene->view_layers.first, OB_MESH);
DEG_relations_tag_update(freestyle_bmain);
#else
Object *object_mesh = NewMesh();
#endif
Mesh *mesh = (Mesh *)object_mesh->data;
mesh->verts_num = group->totvert;
mesh->edges_num = group->totedge;
mesh->faces_num = group->faces_num;
mesh->corners_num = group->totloop;
mesh->totcol = group->materials.size();
BKE_mesh_face_offsets_ensure_alloc(mesh);
blender::bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
MutableSpan<int> face_offsets = mesh->face_offsets_for_write();
bke::SpanAttributeWriter position_attr = attributes.lookup_or_add_for_write_span<float3>(
"position", bke::AttrDomain::Point);
bke::SpanAttributeWriter edge_verts_attr = attributes.lookup_or_add_for_write_span<int2>(
".edge_verts", bke::AttrDomain::Edge);
bke::SpanAttributeWriter corner_vert_attr = attributes.lookup_or_add_for_write_span<int>(
".corner_vert", bke::AttrDomain::Corner);
bke::SpanAttributeWriter corner_edge_attr = attributes.lookup_or_add_for_write_span<int>(
".corner_edge", bke::AttrDomain::Corner);
bke::SpanAttributeWriter material_index_attr = attributes.lookup_or_add_for_write_span<int>(
"material_index", bke::AttrDomain::Face);
float3 *vert_positions = position_attr.span.data();
int2 *edges = edge_verts_attr.span.data();
int *corner_verts = corner_vert_attr.span.data();
int *corner_edges = corner_edge_attr.span.data();
int *material_indices = material_index_attr.span.data();
std::array<bke::SpanAttributeWriter<float2>, 2> uv_map_attrs;
blender::float2 *loopsuv[2] = {nullptr};
if (hasTex) {
// First UV layer
uv_map_attrs[0] = attributes.lookup_or_add_for_write_span<float2>(uvNames[0],
bke::AttrDomain::Corner);
loopsuv[0] = uv_map_attrs[0].span.data();
mesh->uv_maps_active_set(uvNames[0]);
// Second UV layer
uv_map_attrs[1] = attributes.lookup_or_add_for_write_span<float2>(uvNames[1],
bke::AttrDomain::Corner);
loopsuv[1] = uv_map_attrs[1].span.data();
mesh->uv_maps_active_set(uvNames[1]);
}
// colors and transparency (the latter represented by grayscale colors)
bke::SpanAttributeWriter<ColorGeometry4b> colors_attr =
attributes.lookup_or_add_for_write_span<ColorGeometry4b>("Color", bke::AttrDomain::Corner);
ColorGeometry4b *colors = colors_attr.span.data();
bke::SpanAttributeWriter<ColorGeometry4b> transp_attr =
attributes.lookup_or_add_for_write_span<ColorGeometry4b>("Alpha", bke::AttrDomain::Corner);
ColorGeometry4b *transp = transp_attr.span.data();
BKE_id_attributes_active_color_set(&mesh->id, "Color");
mesh->mat = MEM_new_array_uninitialized<Material *>(size_t(mesh->totcol), "MaterialList");
for (const auto item : group->materials.items()) {
Material *material = item.key;
const int matnr = item.value;
mesh->mat[matnr] = material;
if (material) {
id_us_plus(&material->id);
}
}
////////////////////
// Data copy
////////////////////
int vertex_index = 0, edge_index = 0, loop_index = 0, face_index = 0;
int visible_faces, visible_segments;
bool visible;
Strip::vertex_container::iterator v[3];
StrokeVertexRep *svRep[3];
Vec2r p;
for (vector<StrokeRep *>::const_iterator it = group->strokes.begin(),
itend = group->strokes.end();
it != itend;
++it)
{
const int matnr = group->materials.lookup_default((*it)->getMaterial(), 0);
vector<Strip *> &strips = (*it)->getStrips();
for (vector<Strip *>::const_iterator s = strips.begin(), send = strips.end(); s != send; ++s) {
Strip::vertex_container &strip_vertices = (*s)->vertices();
int strip_vertex_count = strip_vertices.size();
// count visible faces and strip segments
test_strip_visibility(strip_vertices, &visible_faces, &visible_segments);
if (visible_faces == 0) {
continue;
}
v[0] = strip_vertices.begin();
v[1] = v[0] + 1;
v[2] = v[0] + 2;
visible = false;
// NOTE: Mesh generation in the following loop assumes stroke strips
// to be triangle strips.
for (int n = 2; n < strip_vertex_count; n++, v[0]++, v[1]++, v[2]++) {
svRep[0] = *(v[0]);
svRep[1] = *(v[1]);
svRep[2] = *(v[2]);
if (!test_triangle_visibility(svRep)) {
visible = false;
}
else {
if (!visible) {
// first vertex
vert_positions[vertex_index][0] = svRep[0]->point2d()[0];
vert_positions[vertex_index][1] = svRep[0]->point2d()[1];
vert_positions[vertex_index][2] = get_stroke_vertex_z();
++vertex_index;
// second vertex
vert_positions[vertex_index][0] = svRep[1]->point2d()[0];
vert_positions[vertex_index][1] = svRep[1]->point2d()[1];
vert_positions[vertex_index][2] = get_stroke_vertex_z();
++vertex_index;
// first edge
edges[edge_index][0] = vertex_index - 2;
edges[edge_index][1] = vertex_index - 1;
++edge_index;
}
visible = true;
// vertex
vert_positions[vertex_index][0] = svRep[2]->point2d()[0];
vert_positions[vertex_index][1] = svRep[2]->point2d()[1];
vert_positions[vertex_index][2] = get_stroke_vertex_z();
++vertex_index;
// edges
edges[edge_index][0] = vertex_index - 1;
edges[edge_index][1] = vertex_index - 3;
++edge_index;
edges[edge_index][0] = vertex_index - 1;
edges[edge_index][1] = vertex_index - 2;
++edge_index;
// poly
face_offsets[face_index] = loop_index;
*material_indices = matnr;
++material_indices;
++face_index;
// Even and odd loops connect triangles vertices differently
bool is_odd = n % 2;
// loops
if (is_odd) {
corner_verts[0] = vertex_index - 1;
corner_edges[0] = edge_index - 2;
corner_verts[1] = vertex_index - 3;
corner_edges[1] = edge_index - 3;
corner_verts[2] = vertex_index - 2;
corner_edges[2] = edge_index - 1;
}
else {
corner_verts[0] = vertex_index - 1;
corner_edges[0] = edge_index - 1;
corner_verts[1] = vertex_index - 2;
corner_edges[1] = edge_index - 3;
corner_verts[2] = vertex_index - 3;
corner_edges[2] = edge_index - 2;
}
corner_verts += 3;
corner_edges += 3;
loop_index += 3;
// UV
if (hasTex) {
// First UV layer (loopsuv[0]) has no tips (texCoord(0)).
// Second UV layer (loopsuv[1]) has tips: (texCoord(1)).
for (int L = 0; L < 2; L++) {
if (is_odd) {
loopsuv[L][0][0] = svRep[2]->texCoord(L).x();
loopsuv[L][0][1] = svRep[2]->texCoord(L).y();
loopsuv[L][1][0] = svRep[0]->texCoord(L).x();
loopsuv[L][1][1] = svRep[0]->texCoord(L).y();
loopsuv[L][2][0] = svRep[1]->texCoord(L).x();
loopsuv[L][2][1] = svRep[1]->texCoord(L).y();
}
else {
loopsuv[L][0][0] = svRep[2]->texCoord(L).x();
loopsuv[L][0][1] = svRep[2]->texCoord(L).y();
loopsuv[L][1][0] = svRep[1]->texCoord(L).x();
loopsuv[L][1][1] = svRep[1]->texCoord(L).y();
loopsuv[L][2][0] = svRep[0]->texCoord(L).x();
loopsuv[L][2][1] = svRep[0]->texCoord(L).y();
}
loopsuv[L] += 3;
}
}
// colors and alpha transparency. vertex colors are in sRGB
// space by convention, so convert from linear
float rgba[3][4];
for (int i = 0; i < 3; i++) {
copy_v3fl_v3db(rgba[i], &svRep[i]->color()[0]);
rgba[i][3] = svRep[i]->alpha();
}
if (is_odd) {
linearrgb_to_srgb_uchar4(&colors[0].r, rgba[2]);
linearrgb_to_srgb_uchar4(&colors[1].r, rgba[0]);
linearrgb_to_srgb_uchar4(&colors[2].r, rgba[1]);
}
else {
linearrgb_to_srgb_uchar4(&colors[0].r, rgba[2]);
linearrgb_to_srgb_uchar4(&colors[1].r, rgba[1]);
linearrgb_to_srgb_uchar4(&colors[2].r, rgba[0]);
}
transp[0].r = transp[0].g = transp[0].b = colors[0].a;
transp[1].r = transp[1].g = transp[1].b = colors[1].a;
transp[2].r = transp[2].g = transp[2].b = colors[2].a;
colors += 3;
transp += 3;
}
} // loop over strip vertices
} // loop over strips
} // loop over strokes
BKE_object_materials_sync_length(freestyle_bmain, object_mesh, (ID *)mesh);
position_attr.finish();
edge_verts_attr.finish();
corner_vert_attr.finish();
corner_edge_attr.finish();
material_index_attr.finish();
uv_map_attrs[0].finish();
uv_map_attrs[1].finish();
colors_attr.finish();
transp_attr.finish();
}
// A replacement of BKE_object_add() for better performance.
blender::Object *BlenderStrokeRenderer::NewMesh() const
{
using namespace blender;
Object *ob;
char name[MAX_ID_NAME];
uint mesh_id = get_stroke_mesh_id();
SNPRINTF(name, "0%08xOB", mesh_id);
ob = BKE_object_add_only_object(freestyle_bmain, OB_MESH, name);
SNPRINTF(name, "0%08xME", mesh_id);
ob->data = blender::id_cast<ID *>(BKE_mesh_add(freestyle_bmain, name));
Collection *collection_master = freestyle_scene->master_collection;
BKE_collection_object_add(freestyle_bmain, collection_master, ob);
DEG_graph_tag_relations_update(freestyle_depsgraph);
DEG_graph_id_tag_update(freestyle_bmain,
freestyle_depsgraph,
&ob->id,
ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
return ob;
}
blender::Render *BlenderStrokeRenderer::RenderScene(blender::Render *re, bool render)
{
using namespace blender;
#if 0
if (blender::G.debug & blender::G_DEBUG_FREESTYLE) {
const Camera *camera = (const Camera *)freestyle_scene->camera->data;
cout << "clip_start " << camera->clip_start << ", clip_end " << camera->clip_end << endl;
}
#endif
Render *freestyle_render = RE_NewSceneRender(freestyle_scene);
DEG_graph_relations_update(freestyle_depsgraph);
freestyle_render->pipeline_depsgraph = re->pipeline_depsgraph;
freestyle_render->pipeline_scene_eval = re->pipeline_scene_eval;
RE_RenderFreestyleStrokes(
freestyle_render, freestyle_bmain, freestyle_scene, render && get_stroke_count() > 0);
freestyle_render->pipeline_depsgraph = nullptr;
freestyle_render->pipeline_scene_eval = nullptr;
return freestyle_render;
}
} /* namespace Freestyle */

View File

@@ -0,0 +1,88 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
*/
#include "BLI_map.hh"
#include "../stroke/StrokeRenderer.h"
#include "../system/FreestyleConfig.h"
namespace blender {
struct Depsgraph;
struct Main;
struct Material;
struct Object;
struct Render;
struct Scene;
struct bContext;
struct bNodeTree;
} // namespace blender
namespace Freestyle {
class BlenderStrokeRenderer : public StrokeRenderer {
public:
BlenderStrokeRenderer(blender::Render *re, int render_count);
virtual ~BlenderStrokeRenderer();
/** Renders a stroke rep */
virtual void RenderStrokeRep(StrokeRep *iStrokeRep) const;
virtual void RenderStrokeRepBasic(StrokeRep *iStrokeRep) const;
blender::Object *NewMesh() const;
struct StrokeGroup {
explicit StrokeGroup() : totvert(0), totedge(0), faces_num(0), totloop(0) {}
vector<StrokeRep *> strokes;
blender::Map<blender::Material *, int> materials;
int totvert;
int totedge;
int faces_num;
int totloop;
};
vector<StrokeGroup *> strokeGroups, texturedStrokeGroups;
int GenerateScene();
void GenerateStrokeMesh(StrokeGroup *group, bool hasTex);
void FreeStrokeGroups();
blender::Render *RenderScene(blender::Render *re, bool render);
static blender::Material *GetStrokeShader(blender::Main *bmain,
blender::bNodeTree *iNodeTree,
bool do_id_user);
protected:
blender::Main *freestyle_bmain;
blender::Scene *old_scene;
blender::Scene *freestyle_scene;
blender::Depsgraph *freestyle_depsgraph;
blender::bContext *_context;
float _width, _height;
float _z, _z_delta;
uint _mesh_id;
bool _use_shading_nodes;
mutable blender::Map<blender::bNodeTree *, blender::Material *> _nodetree_hash;
static const char *uvNames[];
int get_stroke_count() const;
float get_stroke_vertex_z(void) const;
uint get_stroke_mesh_id(void) const;
bool test_triangle_visibility(StrokeVertexRep *svRep[3]) const;
void test_strip_visibility(Strip::vertex_container &strip_vertices,
int *visible_faces,
int *visible_segments) const;
vector<StrokeRep *> _strokeReps;
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:BlenderStrokeRenderer")
};
} /* namespace Freestyle */

View File

@@ -0,0 +1,70 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
*/
#include "../stroke/StyleModule.h"
#include "../system/PythonInterpreter.h"
#include "BLI_utildefines.h" // BLI_assert()
namespace blender {
struct Text;
}
namespace Freestyle {
class BufferedStyleModule : public StyleModule {
public:
BufferedStyleModule(const string &buffer, const string &file_name, Interpreter *inter)
: StyleModule(file_name, inter)
{
_buffer = buffer;
}
virtual ~BufferedStyleModule() {}
protected:
virtual int interpret()
{
PythonInterpreter *py_inter = dynamic_cast<PythonInterpreter *>(_inter);
BLI_assert(py_inter != 0);
return py_inter->interpretString(_buffer, getFileName());
}
private:
string _buffer;
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:BufferedStyleModule")
};
class BlenderStyleModule : public StyleModule {
public:
BlenderStyleModule(blender::Text *text, const string &name, Interpreter *inter)
: StyleModule(name, inter)
{
_text = text;
}
virtual ~BlenderStyleModule() {}
protected:
virtual int interpret()
{
PythonInterpreter *py_inter = dynamic_cast<PythonInterpreter *>(_inter);
BLI_assert(py_inter != 0);
return py_inter->interpretText(_text, getFileName());
}
private:
struct blender::Text *_text;
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:BlenderStyleModule")
};
} /* namespace Freestyle */

View File

@@ -0,0 +1,765 @@
/* SPDX-FileCopyrightText: 2008-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include <iostream>
#include "../application/AppConfig.h"
#include "../application/AppView.h"
#include "../application/Controller.h"
#include "BlenderStrokeRenderer.h"
#include "MEM_guardedalloc.h"
#include "DNA_collection_types.h"
#include "DNA_freestyle_types.h"
#include "DNA_material_types.h"
#include "DNA_text_types.h"
#include "BKE_callbacks.hh"
#include "BKE_context.hh"
#include "BKE_freestyle.h"
#include "BKE_global.hh"
#include "BKE_lib_id.hh"
#include "BKE_linestyle.h"
#include "BKE_scene.hh"
#include "BKE_text.h"
#include "BLT_translation.hh"
#include "BLI_listbase.h"
#include "BLI_math_color_blend.h"
#include "BLI_math_matrix.h"
#include "BLI_math_rotation.h"
#include "BPY_extern.hh"
#include "DEG_depsgraph_query.hh"
#include "IMB_imbuf.hh"
#include "pipeline.hh"
#include "FRS_freestyle.h"
using namespace std;
using namespace Freestyle;
namespace blender {
FreestyleGlobals g_freestyle;
// Freestyle configuration
static bool freestyle_is_initialized = false;
static Config::Path *pathconfig = nullptr;
static Controller *controller = nullptr;
static AppView *view = nullptr;
// line set buffer for copy & paste
static FreestyleLineSet lineset_buffer;
static bool lineset_copied = false;
static void load_post_callback(Main * /*main*/,
PointerRNA ** /*pointers*/,
const int /*num_pointers*/,
void * /*arg*/)
{
lineset_copied = false;
}
static bCallbackFuncStore load_post_callback_funcstore = {
/*next*/ nullptr,
/*prev*/ nullptr,
/*func*/ load_post_callback,
/*arg*/ nullptr,
/*alloc*/ 0};
//=======================================================
// Initialization
//=======================================================
void FRS_init()
{
if (freestyle_is_initialized) {
return;
}
pathconfig = new Config::Path;
controller = new Controller();
view = new AppView;
controller->setView(view);
controller->Clear();
g_freestyle.scene = nullptr;
lineset_copied = false;
BKE_callback_add(&load_post_callback_funcstore, BKE_CB_EVT_LOAD_POST);
freestyle_is_initialized = true;
}
void FRS_set_context(bContext *C)
{
if (G.debug & G_DEBUG_FREESTYLE) {
cout << "FRS_set_context: context 0x" << C << " scene 0x" << CTX_data_scene(C) << endl;
}
controller->setContext(C);
}
void FRS_exit()
{
delete pathconfig;
delete controller;
delete view;
}
//=======================================================
// Rendering
//=======================================================
static void init_view(Render *re)
{
int width = re->winx;
int height = re->winy;
int xmin = re->disprect.xmin;
int ymin = re->disprect.ymin;
int xmax = re->disprect.xmax;
int ymax = re->disprect.ymax;
float thickness = 1.0f;
switch (re->scene->r.line_thickness_mode) {
case R_LINE_THICKNESS_ABSOLUTE:
thickness = re->scene->r.unit_line_thickness * (re->r.size / 100.0f);
break;
case R_LINE_THICKNESS_RELATIVE:
thickness = height / 480.0f;
break;
}
g_freestyle.viewport[0] = 0;
g_freestyle.viewport[1] = 0;
g_freestyle.viewport[2] = width;
g_freestyle.viewport[3] = height;
view->setWidth(width);
view->setHeight(height);
view->setBorder(xmin, ymin, xmax, ymax);
view->setThickness(thickness);
if (G.debug & G_DEBUG_FREESTYLE) {
cout << "\n=== Dimensions of the 2D image coordinate system ===" << endl;
cout << "Width : " << width << endl;
cout << "Height : " << height << endl;
if (re->r.mode & R_BORDER) {
cout << "Border : (" << xmin << ", " << ymin << ") - (" << xmax << ", " << ymax << ")"
<< endl;
}
cout << "Unit line thickness : " << thickness << " pixel(s)" << endl;
}
}
static char *escape_quotes(char *name)
{
char *s = MEM_new_array_uninitialized<char>(strlen(name) * 2 + 1, "escape_quotes");
char *p = s;
while (*name) {
if (*name == '\'') {
*(p++) = '\\';
}
*(p++) = *(name++);
}
*p = '\0';
return s;
}
static char *create_lineset_handler(char *layer_name, char *lineset_name)
{
const char *fmt = "__import__('parameter_editor').process('%s', '%s')\n";
char *s1 = escape_quotes(layer_name);
char *s2 = escape_quotes(lineset_name);
char *text = BLI_sprintfN(fmt, s1, s2);
MEM_delete(s1);
MEM_delete(s2);
return text;
}
struct edge_type_condition {
int edge_type, value;
};
// examines the conditions and returns true if the target edge type needs to be computed
static bool test_edge_type_conditions(edge_type_condition *conditions,
int num_edge_types,
bool logical_and,
int target,
bool distinct)
{
int target_condition = 0;
int num_non_target_positive_conditions = 0;
int num_non_target_negative_conditions = 0;
for (int i = 0; i < num_edge_types; i++) {
if (conditions[i].edge_type == target) {
target_condition = conditions[i].value;
}
else if (conditions[i].value > 0) {
++num_non_target_positive_conditions;
}
else if (conditions[i].value < 0) {
++num_non_target_negative_conditions;
}
}
if (distinct) {
// In this case, the 'target' edge type is assumed to appear on distinct edge
// of its own and never together with other edge types.
if (logical_and) {
if (num_non_target_positive_conditions > 0) {
return false;
}
if (target_condition > 0) {
return true;
}
if (target_condition < 0) {
return false;
}
if (num_non_target_negative_conditions > 0) {
return true;
}
}
else {
if (target_condition > 0) {
return true;
}
if (num_non_target_negative_conditions > 0) {
return true;
}
if (target_condition < 0) {
return false;
}
if (num_non_target_positive_conditions > 0) {
return false;
}
}
}
else {
// In this case, the 'target' edge type may appear together with other edge types.
if (target_condition > 0) {
return true;
}
if (target_condition < 0) {
return true;
}
if (logical_and) {
if (num_non_target_positive_conditions > 0) {
return false;
}
if (num_non_target_negative_conditions > 0) {
return true;
}
}
else {
if (num_non_target_negative_conditions > 0) {
return true;
}
if (num_non_target_positive_conditions > 0) {
return false;
}
}
}
return true;
}
static void prepare(Render *re, ViewLayer *view_layer, Depsgraph *depsgraph)
{
// load mesh
re->i.infostr = RPT_("Freestyle: Mesh loading");
re->display->stats_draw(&re->i);
re->i.infostr = nullptr;
if (controller->LoadMesh(re, view_layer, depsgraph)) {
/* Returns if scene cannot be loaded or if empty. */
return;
}
if (re->display->test_break()) {
return;
}
// add style modules
FreestyleConfig *config = &view_layer->freestyle_config;
if (G.debug & G_DEBUG_FREESTYLE) {
cout << "\n=== Rendering options ===" << endl;
}
int layer_count = 0;
switch (config->mode) {
case FREESTYLE_CONTROL_SCRIPT_MODE:
if (G.debug & G_DEBUG_FREESTYLE) {
cout << "Modules :" << endl;
}
for (FreestyleModuleConfig &module_conf : config->modules) {
if (module_conf.script && module_conf.is_displayed) {
const char *id_name = module_conf.script->id.name + 2;
if (G.debug & G_DEBUG_FREESTYLE) {
cout << " " << layer_count + 1 << ": " << id_name;
if (module_conf.script->filepath) {
cout << " (" << module_conf.script->filepath << ")";
}
cout << endl;
}
controller->InsertStyleModule(layer_count, id_name, module_conf.script);
controller->toggleLayer(layer_count, true);
layer_count++;
}
}
if (G.debug & G_DEBUG_FREESTYLE) {
cout << endl;
}
controller->setComputeRidgesAndValleysFlag(
(config->flags & FREESTYLE_RIDGES_AND_VALLEYS_FLAG) ? true : false);
controller->setComputeSuggestiveContoursFlag(
(config->flags & FREESTYLE_SUGGESTIVE_CONTOURS_FLAG) ? true : false);
controller->setComputeMaterialBoundariesFlag(
(config->flags & FREESTYLE_MATERIAL_BOUNDARIES_FLAG) ? true : false);
break;
case FREESTYLE_CONTROL_EDITOR_MODE:
int use_ridges_and_valleys = 0;
int use_suggestive_contours = 0;
int use_material_boundaries = 0;
edge_type_condition conditions[] = {
{FREESTYLE_FE_SILHOUETTE, 0},
{FREESTYLE_FE_BORDER, 0},
{FREESTYLE_FE_CREASE, 0},
{FREESTYLE_FE_RIDGE_VALLEY, 0},
{FREESTYLE_FE_SUGGESTIVE_CONTOUR, 0},
{FREESTYLE_FE_MATERIAL_BOUNDARY, 0},
{FREESTYLE_FE_CONTOUR, 0},
{FREESTYLE_FE_EXTERNAL_CONTOUR, 0},
{FREESTYLE_FE_EDGE_MARK, 0},
};
int num_edge_types = ARRAY_SIZE(conditions);
if (G.debug & G_DEBUG_FREESTYLE) {
cout << "Linesets:" << endl;
}
for (FreestyleLineSet &lineset : config->linesets) {
if (lineset.flags & FREESTYLE_LINESET_ENABLED) {
if (G.debug & G_DEBUG_FREESTYLE) {
cout << " " << layer_count + 1 << ": " << lineset.name << " - "
<< (lineset.linestyle ? (lineset.linestyle->id.name + 2) : "<null>") << endl;
}
char *buffer = create_lineset_handler(view_layer->name, lineset.name);
controller->InsertStyleModule(layer_count, lineset.name, buffer);
controller->toggleLayer(layer_count, true);
MEM_delete(buffer);
if (!(lineset.selection & FREESTYLE_SEL_EDGE_TYPES) || !lineset.edge_types) {
++use_ridges_and_valleys;
++use_suggestive_contours;
++use_material_boundaries;
}
else {
// conditions for feature edge selection by edge types
for (int i = 0; i < num_edge_types; i++) {
if (!(lineset.edge_types & conditions[i].edge_type)) {
conditions[i].value = 0; // no condition specified
}
else if (!(lineset.exclude_edge_types & conditions[i].edge_type)) {
conditions[i].value = 1; // condition: X
}
else {
conditions[i].value = -1; // condition: NOT X
}
}
// logical operator for the selection conditions
bool logical_and = ((lineset.flags & FREESTYLE_LINESET_FE_AND) != 0);
// negation operator
if (lineset.flags & FREESTYLE_LINESET_FE_NOT) {
// convert an Exclusive condition into an
// Inclusive equivalent using De Morgan's laws:
// - NOT (X OR Y) --> (NOT X) AND (NOT Y)
// - NOT (X AND Y) --> (NOT X) OR (NOT Y)
for (int i = 0; i < num_edge_types; i++) {
conditions[i].value *= -1;
}
logical_and = !logical_and;
}
if (test_edge_type_conditions(
conditions, num_edge_types, logical_and, FREESTYLE_FE_RIDGE_VALLEY, true))
{
++use_ridges_and_valleys;
}
if (test_edge_type_conditions(conditions,
num_edge_types,
logical_and,
FREESTYLE_FE_SUGGESTIVE_CONTOUR,
true))
{
++use_suggestive_contours;
}
if (test_edge_type_conditions(
conditions, num_edge_types, logical_and, FREESTYLE_FE_MATERIAL_BOUNDARY, true))
{
++use_material_boundaries;
}
}
layer_count++;
}
}
controller->setComputeRidgesAndValleysFlag(use_ridges_and_valleys > 0);
controller->setComputeSuggestiveContoursFlag(use_suggestive_contours > 0);
controller->setComputeMaterialBoundariesFlag(use_material_boundaries > 0);
break;
}
// set parameters
controller->setSphereRadius(config->sphere_radius);
controller->setSuggestiveContourKrDerivativeEpsilon(config->dkr_epsilon);
controller->setFaceSmoothness((config->flags & FREESTYLE_FACE_SMOOTHNESS_FLAG) ? true : false);
controller->setCreaseAngle(RAD2DEGF(config->crease_angle));
controller->setVisibilityAlgo((config->flags & FREESTYLE_CULLING) ?
FREESTYLE_ALGO_CULLED_ADAPTIVE_CUMULATIVE :
FREESTYLE_ALGO_ADAPTIVE_CUMULATIVE);
if (G.debug & G_DEBUG_FREESTYLE) {
cout << "Crease angle : " << controller->getCreaseAngle() << endl;
cout << "Sphere radius : " << controller->getSphereRadius() << endl;
cout << "Face smoothness : " << (controller->getFaceSmoothness() ? "enabled" : "disabled")
<< endl;
cout << "Ridges and valleys : "
<< (controller->getComputeRidgesAndValleysFlag() ? "enabled" : "disabled") << endl;
cout << "Suggestive contours : "
<< (controller->getComputeSuggestiveContoursFlag() ? "enabled" : "disabled") << endl;
cout << "Suggestive contour Kr derivative epsilon : "
<< controller->getSuggestiveContourKrDerivativeEpsilon() << endl;
cout << "Material boundaries : "
<< (controller->getComputeMaterialBoundariesFlag() ? "enabled" : "disabled") << endl;
cout << endl;
}
// set diffuse and z depth passes
RenderLayer *rl = RE_GetRenderLayer(re->result, view_layer->name);
bool diffuse = false, z = false;
for (RenderPass &rpass : rl->passes) {
float *rpass_buffer_data = rpass.ibuf->float_data_for_write();
if (STREQ(rpass.name, RE_PASSNAME_DIFFUSE_COLOR)) {
controller->setPassDiffuse(rpass_buffer_data, rpass.rectx, rpass.recty);
diffuse = true;
}
if (STREQ(rpass.name, RE_PASSNAME_DEPTH)) {
controller->setPassZ(rpass_buffer_data, rpass.rectx, rpass.recty);
z = true;
}
}
if (G.debug & G_DEBUG_FREESTYLE) {
cout << "Passes :" << endl;
cout << " Diffuse = " << (diffuse ? "enabled" : "disabled") << endl;
cout << " Z = " << (z ? "enabled" : "disabled") << endl;
}
if (controller->hitViewMapCache()) {
return;
}
// compute view map
re->i.infostr = RPT_("Freestyle: View map creation");
re->display->stats_draw(&re->i);
re->i.infostr = nullptr;
controller->ComputeViewMap();
}
void FRS_composite_result(Render *re, ViewLayer *view_layer, Render *freestyle_render)
{
RenderLayer *rl;
float *src, *dest, *pixSrc, *pixDest;
int x, y, rectx, recty;
if (freestyle_render == nullptr || freestyle_render->result == nullptr) {
if (view_layer->freestyle_config.flags & FREESTYLE_AS_RENDER_PASS) {
// Create a blank render pass output.
RE_create_render_pass(
re->result, RE_PASSNAME_FREESTYLE, 4, "RGBA", view_layer->name, re->viewname, true);
}
return;
}
rl = render_get_single_layer(freestyle_render, freestyle_render->result);
if (!rl) {
if (G.debug & G_DEBUG_FREESTYLE) {
cout << "No source render layer to composite" << endl;
}
return;
}
src = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, freestyle_render->viewname);
if (!src) {
if (G.debug & G_DEBUG_FREESTYLE) {
cout << "No source result image to composite" << endl;
}
return;
}
#if 0
if (G.debug & G_DEBUG_FREESTYLE) {
cout << "src: " << rl->rectx << " x " << rl->recty << endl;
}
#endif
rl = RE_GetRenderLayer(re->result, view_layer->name);
if (!rl) {
if (G.debug & G_DEBUG_FREESTYLE) {
cout << "No destination render layer to composite to" << endl;
}
return;
}
if (view_layer->freestyle_config.flags & FREESTYLE_AS_RENDER_PASS) {
RE_create_render_pass(
re->result, RE_PASSNAME_FREESTYLE, 4, "RGBA", view_layer->name, re->viewname, true);
dest = RE_RenderLayerGetPass(rl, RE_PASSNAME_FREESTYLE, re->viewname);
}
else {
dest = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, re->viewname);
}
if (!dest) {
if (G.debug & G_DEBUG_FREESTYLE) {
cout << "No destination result image to composite to" << endl;
}
return;
}
#if 0
if (G.debug & G_DEBUG_FREESTYLE) {
cout << "dest: " << rl->rectx << " x " << rl->recty << endl;
}
#endif
rectx = re->rectx;
recty = re->recty;
for (y = 0; y < recty; y++) {
for (x = 0; x < rectx; x++) {
pixSrc = src + 4 * (rectx * y + x);
if (pixSrc[3] > 0.0) {
pixDest = dest + 4 * (rectx * y + x);
blend_color_mix_float(pixDest, pixDest, pixSrc);
}
}
}
}
static int displayed_layer_count(ViewLayer *view_layer)
{
int count = 0;
switch (view_layer->freestyle_config.mode) {
case FREESTYLE_CONTROL_SCRIPT_MODE:
for (FreestyleModuleConfig &module : view_layer->freestyle_config.modules) {
if (module.script && module.is_displayed) {
count++;
}
}
break;
case FREESTYLE_CONTROL_EDITOR_MODE:
for (FreestyleLineSet &lineset : view_layer->freestyle_config.linesets) {
if (lineset.flags & FREESTYLE_LINESET_ENABLED) {
count++;
}
}
break;
}
return count;
}
int FRS_is_freestyle_enabled(ViewLayer *view_layer)
{
return ((view_layer->flag & VIEW_LAYER_RENDER) && (view_layer->flag & VIEW_LAYER_FREESTYLE) &&
displayed_layer_count(view_layer) > 0);
}
void FRS_init_stroke_renderer(Render *re)
{
if (G.debug & G_DEBUG_FREESTYLE) {
cout << endl;
cout << "#===============================================================" << endl;
cout << "# Freestyle" << endl;
cout << "#===============================================================" << endl;
}
init_view(re);
controller->ResetRenderCount();
}
void FRS_begin_stroke_rendering(Render * /*re*/) {}
void FRS_do_stroke_rendering(Render *re, ViewLayer *view_layer)
{
RenderMonitor monitor(re);
controller->setRenderMonitor(&monitor);
controller->setViewMapCache(
(view_layer->freestyle_config.flags & FREESTYLE_VIEW_MAP_CACHE) ? true : false);
if (G.debug & G_DEBUG_FREESTYLE) {
cout << endl;
cout << "----------------------------------------------------------" << endl;
cout << "| " << (re->scene->id.name + 2) << "|" << view_layer->name << endl;
cout << "----------------------------------------------------------" << endl;
}
/* Create depsgraph and evaluate scene. */
ViewLayer *scene_view_layer = (ViewLayer *)BLI_findstring(
&re->scene->view_layers, view_layer->name, offsetof(ViewLayer, name));
Depsgraph *depsgraph = DEG_graph_new(re->main, re->scene, scene_view_layer, DAG_EVAL_RENDER);
BKE_scene_graph_update_for_newframe(depsgraph);
/* Init camera
* Objects are transformed into camera coordinate system, therefore the camera position
* is zero and the model-view matrix is the identity matrix. */
Object *ob_camera_orig = RE_GetCamera(re);
Object *ob_camera_eval = DEG_get_evaluated(depsgraph, ob_camera_orig);
zero_v3(g_freestyle.viewpoint);
unit_m4(g_freestyle.mv);
RE_GetCameraWindow(re, ob_camera_eval, g_freestyle.proj);
// prepare Freestyle:
// - load mesh
// - add style modules
// - set parameters
// - compute view map
prepare(re, view_layer, depsgraph);
if (re->display->test_break()) {
controller->CloseFile();
if (G.debug & G_DEBUG_FREESTYLE) {
cout << "Break" << endl;
}
}
else {
// render and composite Freestyle result
if (controller->_ViewMap) {
// render strokes
re->i.infostr = RPT_("Freestyle: Stroke rendering");
re->display->stats_draw(&re->i);
re->i.infostr = nullptr;
g_freestyle.scene = DEG_get_evaluated_scene(depsgraph);
int strokeCount = controller->DrawStrokes();
Render *freestyle_render = nullptr;
if (strokeCount > 0) {
freestyle_render = controller->RenderStrokes(re, true);
}
controller->CloseFile();
g_freestyle.scene = nullptr;
// composite result
FRS_composite_result(re, view_layer, freestyle_render);
if (freestyle_render) {
RE_FreeRender(freestyle_render);
}
}
}
DEG_graph_free(depsgraph);
}
void FRS_end_stroke_rendering(Render * /*re*/)
{
// clear canvas
controller->Clear();
}
void FRS_free_view_map_cache()
{
// free cache
controller->DeleteViewMap(true);
#if 0
if (G.debug & G_DEBUG_FREESTYLE) {
printf("View map cache freed\n");
}
#endif
}
//=======================================================
// Freestyle Panel Configuration
//=======================================================
void FRS_copy_active_lineset(FreestyleConfig *config)
{
FreestyleLineSet *lineset = BKE_freestyle_lineset_get_active(config);
if (lineset) {
lineset_buffer.linestyle = lineset->linestyle;
lineset_buffer.flags = lineset->flags;
lineset_buffer.selection = lineset->selection;
lineset_buffer.qi = lineset->qi;
lineset_buffer.qi_start = lineset->qi_start;
lineset_buffer.qi_end = lineset->qi_end;
lineset_buffer.edge_types = lineset->edge_types;
lineset_buffer.exclude_edge_types = lineset->exclude_edge_types;
lineset_buffer.group = lineset->group;
STRNCPY(lineset_buffer.name, lineset->name);
lineset_copied = true;
}
}
void FRS_paste_active_lineset(FreestyleConfig *config)
{
if (!lineset_copied) {
return;
}
FreestyleLineSet *lineset = BKE_freestyle_lineset_get_active(config);
if (lineset) {
if (lineset->linestyle) {
id_us_min(&lineset->linestyle->id);
}
lineset->linestyle = lineset_buffer.linestyle;
if (lineset->linestyle) {
id_us_plus(&lineset->linestyle->id);
}
lineset->flags = lineset_buffer.flags;
lineset->selection = lineset_buffer.selection;
lineset->qi = lineset_buffer.qi;
lineset->qi_start = lineset_buffer.qi_start;
lineset->qi_end = lineset_buffer.qi_end;
lineset->edge_types = lineset_buffer.edge_types;
lineset->exclude_edge_types = lineset_buffer.exclude_edge_types;
if (lineset->group) {
id_us_min(&lineset->group->id);
lineset->group = nullptr;
}
if (lineset_buffer.group) {
lineset->group = lineset_buffer.group;
id_us_plus(&lineset->group->id);
}
STRNCPY(lineset->name, lineset_buffer.name);
BKE_freestyle_lineset_unique_name(config, lineset);
lineset->flags |= FREESTYLE_LINESET_CURRENT;
}
}
void FRS_delete_active_lineset(FreestyleConfig *config)
{
FreestyleLineSet *lineset = BKE_freestyle_lineset_get_active(config);
if (lineset) {
BKE_freestyle_lineset_delete(config, lineset);
}
}
bool FRS_move_active_lineset(FreestyleConfig *config, int direction)
{
FreestyleLineSet *lineset = BKE_freestyle_lineset_get_active(config);
return (lineset != nullptr) && BLI_listbase_link_move(&config->linesets, lineset, direction);
}
// Testing
Material *FRS_create_stroke_material(Main *bmain, FreestyleLineStyle *linestyle)
{
bNodeTree *nt = (linestyle->use_nodes) ? linestyle->nodetree : nullptr;
Material *ma = BlenderStrokeRenderer::GetStrokeShader(bmain, nt, true);
ma->id.us = 0;
return ma;
}
} // namespace blender

View File

@@ -0,0 +1,141 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief A class to hold a bounding box
*/
#include <algorithm>
#include <stdlib.h>
#include "BLI_utildefines.h"
#include "MEM_guardedalloc.h"
namespace Freestyle {
template<class Point> class BBox {
public:
inline BBox()
{
_empty = true;
}
template<class T> inline BBox(const T &min_in, const T &max_in) : _min(min_in), _max(max_in)
{
_empty = false;
}
template<class T> inline BBox(const BBox<T> &b) : _min(b.getMin()), _max(b.getMax())
{
_empty = false;
}
template<class T> inline void extendToContain(const T &p)
{
if (_empty) {
_min = p;
_max = p;
_empty = false;
return;
}
for (uint i = 0; i < Point::dim(); i++) {
if (p[i] < _min[i]) {
_min[i] = p[i];
}
else if (p[i] > _max[i]) {
_max[i] = p[i];
}
}
_empty = false;
}
inline void clear()
{
_empty = true;
}
inline bool empty() const
{
return _empty;
}
inline const Point &getMin() const
{
return _min;
}
inline const Point &getMax() const
{
return _max;
}
inline BBox<Point> &operator=(const BBox<Point> &b)
{
BLI_assert(!b.empty());
_min = b.getMin();
_max = b.getMax();
_empty = false;
return *this;
}
inline BBox<Point> &operator+=(const BBox<Point> &b)
{
BLI_assert(!b.empty());
if (_empty) {
_min = b.getMin();
_max = b.getMax();
_empty = false;
}
else {
for (uint i = 0; i < Point::dim(); i++) {
if (b.getMin()[i] < _min[i]) {
_min[i] = b.getMin()[i];
}
if (b.getMax()[i] > _max[i]) {
_max[i] = b.getMax()[i];
}
}
}
return *this;
}
inline bool inside(const Point &p)
{
if (empty()) {
return false;
}
for (uint i = 0; i < Point::dim(); i++) {
if ((_min[i] > p[i]) || (_max[i] < p[i])) {
return false;
}
}
return true;
}
private:
Point _min;
Point _max;
bool _empty;
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:BBox")
};
template<class Point> BBox<Point> &operator+(const BBox<Point> &b1, const BBox<Point> &b2)
{
Point new_min;
Point new_max;
for (uint i = 0; i < Point::dim(); i++) {
new_min[i] = b1.getMin()[i] < b2.getMin()[i] ? b1.getMin()[i] : b2.getMin()[i];
new_max[i] = b1.getMax()[i] > b2.getMax()[i] ? b1.getMax()[i] : b2.getMax()[i];
}
return BBox<Point>(new_min, new_max);
}
} /* namespace Freestyle */

View File

@@ -0,0 +1,108 @@
/* SPDX-FileCopyrightText: 2012-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
* \brief Class to define a Bezier curve of order 4.
*/
#include "Bezier.h"
#include "FitCurve.h"
using namespace std;
namespace Freestyle {
BezierCurveSegment::~BezierCurveSegment() = default;
void BezierCurveSegment::AddControlPoint(const Vec2d &iPoint)
{
_ControlPolygon.push_back(iPoint);
if (_ControlPolygon.size() == 4) {
Build();
}
}
void BezierCurveSegment::Build()
{
if (_ControlPolygon.size() != 4) {
return;
}
// Compute the rightmost part of the matrix:
vector<Vec2d>::const_iterator p0, p1, p2, p3;
p0 = _ControlPolygon.begin();
p1 = p0;
++p1;
p2 = p1;
++p2;
p3 = p2;
++p3;
float x[4], y[4];
x[0] = -p0->x() + 3 * p1->x() - 3 * p2->x() + p3->x();
x[1] = 3 * p0->x() - 6 * p1->x() + 3 * p2->x();
x[2] = -3 * p0->x() + 3 * p1->x();
x[3] = p0->x();
y[0] = -p0->y() + 3 * p1->y() - 3 * p2->y() + p3->y();
y[1] = 3 * p0->y() - 6 * p1->y() + 3 * p2->y();
y[2] = -3 * p0->y() + 3 * p1->y();
y[3] = p0->y();
int nvertices = 12;
float increment = 1.0 / float(nvertices);
float t = 0.0f;
for (int i = 0; i <= nvertices; ++i) {
_Vertices.emplace_back((x[3] + t * (x[2] + t * (x[1] + t * x[0]))),
(y[3] + t * (y[2] + t * (y[1] + t * y[0]))));
t += increment;
}
}
BezierCurve::BezierCurve()
{
_currentSegment = new BezierCurveSegment;
}
BezierCurve::BezierCurve(vector<Vec2d> &iPoints, double error)
{
FitCurveWrapper fitcurve;
_currentSegment = new BezierCurveSegment;
vector<Vec2d> curve;
fitcurve.FitCurve(iPoints, curve, error);
int i = 0;
vector<Vec2d>::iterator v, vend;
for (v = curve.begin(), vend = curve.end(); v != vend; ++v) {
if ((i == 0) || (i % 4 != 0)) {
AddControlPoint(*v);
}
++i;
}
}
BezierCurve::~BezierCurve()
{
if (!_Segments.empty()) {
vector<BezierCurveSegment *>::iterator v, vend;
for (v = _Segments.begin(), vend = _Segments.end(); v != vend; ++v) {
delete *v;
}
}
delete _currentSegment;
}
void BezierCurve::AddControlPoint(const Vec2d &iPoint)
{
_ControlPolygon.push_back(iPoint);
_currentSegment->AddControlPoint(iPoint);
if (_currentSegment->size() == 4) {
_Segments.push_back(_currentSegment);
_currentSegment = new BezierCurveSegment;
_currentSegment->AddControlPoint(iPoint);
}
}
} /* namespace Freestyle */

View File

@@ -0,0 +1,74 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Class to define a Bezier curve of order 4.
*/
#include <vector>
#include "Geom.h"
#include "../system/FreestyleConfig.h"
#include "MEM_guardedalloc.h"
namespace Freestyle {
using namespace Geometry;
class BezierCurveSegment {
private:
std::vector<Vec2d> _ControlPolygon;
std::vector<Vec2d> _Vertices;
public:
virtual ~BezierCurveSegment();
void AddControlPoint(const Vec2d &iPoint);
void Build();
inline int size() const
{
return _ControlPolygon.size();
}
inline std::vector<Vec2d> &vertices()
{
return _Vertices;
}
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:BezierCurveSegment")
};
class BezierCurve {
private:
std::vector<Vec2d> _ControlPolygon;
std::vector<BezierCurveSegment *> _Segments;
BezierCurveSegment *_currentSegment;
public:
BezierCurve();
BezierCurve(std::vector<Vec2d> &iPoints, double error = 4.0);
virtual ~BezierCurve();
void AddControlPoint(const Vec2d &iPoint);
std::vector<Vec2d> &controlPolygon()
{
return _ControlPolygon;
}
std::vector<BezierCurveSegment *> &segments()
{
return _Segments;
}
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:BezierCurve")
};
} /* namespace Freestyle */

View File

@@ -0,0 +1,71 @@
/* SPDX-FileCopyrightText: 2009-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
* \brief Class to define a cell grid surrounding the bounding box of the scene
*/
#include <cstdlib>
#include "FastGrid.h"
#include "BKE_global.hh"
#include "BLI_utildefines.h"
namespace Freestyle {
void FastGrid::clear()
{
if (!_cells) {
return;
}
for (uint i = 0; i < _cells_size; i++) {
if (_cells[i]) {
delete _cells[i];
}
}
delete[] _cells;
_cells = nullptr;
_cells_size = 0;
Grid::clear();
}
void FastGrid::configure(const Vec3r &orig, const Vec3r &size, uint nb)
{
Grid::configure(orig, size, nb);
_cells_size = _cells_nb[0] * _cells_nb[1] * _cells_nb[2];
_cells = new Cell *[_cells_size];
memset(_cells, 0, _cells_size * sizeof(*_cells));
}
Cell *FastGrid::getCell(const Vec3u &p)
{
#if 0
if (blender::G.debug & blender::G_DEBUG_FREESTYLE) {
cout << _cells << " " << p << " " << _cells_nb[0] << "-" << _cells_nb[1] << "-" << _cells_nb[2]
<< " " << _cells_size << endl;
}
#endif
BLI_assert_msg(_cells, "_cells is a null pointer");
BLI_assert((_cells_nb[0] * (p[2] * _cells_nb[1] + p[1]) + p[0]) < _cells_size);
BLI_assert(p[0] < _cells_nb[0]);
BLI_assert(p[1] < _cells_nb[1]);
BLI_assert(p[2] < _cells_nb[2]);
return _cells[_cells_nb[0] * (p[2] * _cells_nb[1] + p[1]) + p[0]];
}
void FastGrid::fillCell(const Vec3u &p, Cell &cell)
{
BLI_assert_msg(_cells, "_cells is a null pointer");
BLI_assert((_cells_nb[0] * (p[2] * _cells_nb[1] + p[1]) + p[0]) < _cells_size);
BLI_assert(p[0] < _cells_nb[0]);
BLI_assert(p[1] < _cells_nb[1]);
BLI_assert(p[2] < _cells_nb[2]);
_cells[_cells_nb[0] * (p[2] * _cells_nb[1] + p[1]) + p[0]] = &cell;
}
} /* namespace Freestyle */

View File

@@ -0,0 +1,63 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Class to define a cell grid surrounding the bounding box of the scene
*/
#include "Grid.h"
namespace Freestyle {
/** Class to define a regular grid used for ray casting computations
* We don't use a hash-table here. The grid is explicitly stored for faster computations.
* However, this might result in significant increase in memory usage
* (compared to the regular grid).
*/
class FastGrid : public Grid {
public:
FastGrid() : Grid()
{
_cells = nullptr;
_cells_size = 0;
}
virtual ~FastGrid()
{
clear();
}
/**
* clears the grid
* Deletes all the cells, clears the hash-table, resets size, size of cell, number of cells.
*/
virtual void clear();
/** Sets the different parameters of the grid
* orig
* The grid origin
* size
* The grid's dimensions
* nb
* The number of cells of the grid
*/
virtual void configure(const Vec3r &orig, const Vec3r &size, uint nb);
/** returns the cell whose coordinates are passed as argument */
Cell *getCell(const Vec3u &p);
/** Fills the case p with the cell iCell */
virtual void fillCell(const Vec3u &p, Cell &cell);
protected:
Cell **_cells;
uint _cells_size;
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:FastGrid")
};
} /* namespace Freestyle */

View File

@@ -0,0 +1,569 @@
/* SPDX-FileCopyrightText: 2008-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
* \brief An Algorithm for Automatically Fitting Digitized Curves by Philip J. Schneider,
* \brief from "Graphics Gems", Academic Press, 1990
*/
#include <cmath>
#include <cstdio>
#include <cstdlib> // for malloc and free
#include "FitCurve.h"
#include "BLI_sys_types.h"
using namespace std;
namespace Freestyle {
using BezierCurve = Vector2 *;
/* Forward declarations */
static double *Reparameterize(Vector2 *d, int first, int last, double *u, BezierCurve bezCurve);
static double NewtonRaphsonRootFind(BezierCurve Q, Vector2 P, double u);
static Vector2 BezierII(int degree, Vector2 *V, double t);
static double B0(double u);
static double B1(double u);
static double B2(double u);
static double B3(double u);
static Vector2 ComputeLeftTangent(Vector2 *d, int end);
static double ComputeMaxError(
Vector2 *d, int first, int last, BezierCurve bezCurve, double *u, int *splitPoint);
static double *ChordLengthParameterize(Vector2 *d, int first, int last);
static BezierCurve GenerateBezier(
Vector2 *d, int first, int last, double *uPrime, Vector2 tHat1, Vector2 tHat2);
static Vector2 V2AddII(Vector2 a, Vector2 b);
static Vector2 V2ScaleIII(Vector2 v, double s);
static Vector2 V2SubII(Vector2 a, Vector2 b);
/* returns squared length of input vector */
static double V2SquaredLength(Vector2 *a)
{
return (((*a)[0] * (*a)[0]) + ((*a)[1] * (*a)[1]));
}
/* returns length of input vector */
static double V2Length(Vector2 *a)
{
return sqrt(V2SquaredLength(a));
}
static Vector2 *V2Scale(Vector2 *v, double newlen)
{
double len = V2Length(v);
if (len != 0.0) {
(*v)[0] *= newlen / len;
(*v)[1] *= newlen / len;
}
return v;
}
/* return the dot product of vectors a and b */
static double V2Dot(Vector2 *a, Vector2 *b)
{
return (((*a)[0] * (*b)[0]) + ((*a)[1] * (*b)[1]));
}
/* return the distance between two points */
static double V2DistanceBetween2Points(Vector2 *a, Vector2 *b)
{
double dx = (*a)[0] - (*b)[0];
double dy = (*a)[1] - (*b)[1];
return sqrt((dx * dx) + (dy * dy));
}
/* return vector sum c = a+b */
static Vector2 *V2Add(Vector2 *a, Vector2 *b, Vector2 *c)
{
(*c)[0] = (*a)[0] + (*b)[0];
(*c)[1] = (*a)[1] + (*b)[1];
return c;
}
/* normalizes the input vector and returns it */
static Vector2 *V2Normalize(Vector2 *v)
{
double len = V2Length(v);
if (len != 0.0) {
(*v)[0] /= len;
(*v)[1] /= len;
}
return v;
}
/* negates the input vector and returns it */
static Vector2 *V2Negate(Vector2 *v)
{
(*v)[0] = -(*v)[0];
(*v)[1] = -(*v)[1];
return v;
}
/* GenerateBezier:
* Use least-squares method to find Bezier control points for region.
* Vector2 *d; Array of digitized points
* int first, last; Indices defining region
* double *uPrime; Parameter values for region
* Vector2 tHat1, tHat2; Unit tangents at endpoints
*/
static BezierCurve GenerateBezier(
Vector2 *d, int first, int last, double *uPrime, Vector2 tHat1, Vector2 tHat2)
{
int i;
Vector2 A[2]; /* rhs for eqn */
int nPts; /* Number of pts in sub-curve */
double C[2][2]; /* Matrix C */
double X[2]; /* Matrix X */
double det_C0_C1; /* Determinants of matrices */
double det_C0_X;
double det_X_C1;
double alpha_l; /* Alpha values, left and right */
double alpha_r;
Vector2 tmp; /* Utility variable */
BezierCurve bezCurve; /* RETURN bezier curve control points. */
bezCurve = (Vector2 *)malloc(4 * sizeof(Vector2));
nPts = last - first + 1;
/* Create the C and X matrices */
C[0][0] = 0.0;
C[0][1] = 0.0;
C[1][0] = 0.0;
C[1][1] = 0.0;
X[0] = 0.0;
X[1] = 0.0;
for (i = 0; i < nPts; i++) {
/* Compute the A's */
A[0] = tHat1;
A[1] = tHat2;
V2Scale(&A[0], B1(uPrime[i]));
V2Scale(&A[1], B2(uPrime[i]));
C[0][0] += V2Dot(&A[0], &A[0]);
C[0][1] += V2Dot(&A[0], &A[1]);
// C[1][0] += V2Dot(&A[0], &A[1]);
C[1][0] = C[0][1];
C[1][1] += V2Dot(&A[1], &A[1]);
tmp = V2SubII(d[first + i],
V2AddII(V2ScaleIII(d[first], B0(uPrime[i])),
V2AddII(V2ScaleIII(d[first], B1(uPrime[i])),
V2AddII(V2ScaleIII(d[last], B2(uPrime[i])),
V2ScaleIII(d[last], B3(uPrime[i]))))));
X[0] += V2Dot(&A[0], &tmp);
X[1] += V2Dot(&A[1], &tmp);
}
/* Compute the determinants of C and X */
det_C0_C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1];
det_C0_X = C[0][0] * X[1] - C[0][1] * X[0];
det_X_C1 = X[0] * C[1][1] - X[1] * C[0][1];
/* Finally, derive alpha values */
if (det_C0_C1 == 0.0) {
det_C0_C1 = (C[0][0] * C[1][1]) * 10.0e-12;
}
alpha_l = det_X_C1 / det_C0_C1;
alpha_r = det_C0_X / det_C0_C1;
/* If alpha negative, use the Wu/Barsky heuristic (see text) (if alpha is 0, you get coincident
* control points that lead to divide by zero in any subsequent NewtonRaphsonRootFind() call).
*/
if (alpha_l < 1.0e-6 || alpha_r < 1.0e-6) {
double dist = V2DistanceBetween2Points(&d[last], &d[first]) / 3.0;
bezCurve[0] = d[first];
bezCurve[3] = d[last];
V2Add(&(bezCurve[0]), V2Scale(&(tHat1), dist), &(bezCurve[1]));
V2Add(&(bezCurve[3]), V2Scale(&(tHat2), dist), &(bezCurve[2]));
return bezCurve;
}
/* First and last control points of the Bezier curve are positioned exactly at the first and last
* data points Control points 1 and 2 are positioned an alpha distance out on the tangent
* vectors, left and right, respectively
*/
bezCurve[0] = d[first];
bezCurve[3] = d[last];
V2Add(&bezCurve[0], V2Scale(&tHat1, alpha_l), &bezCurve[1]);
V2Add(&bezCurve[3], V2Scale(&tHat2, alpha_r), &bezCurve[2]);
return bezCurve;
}
/*
* Reparameterize:
* Given set of points and their parameterization, try to find a better parameterization.
* Vector2 *d; Array of digitized points
* int first, last; Indices defining region
* double *u; Current parameter values
* BezierCurve bezCurve; Current fitted curve
*/
static double *Reparameterize(Vector2 *d, int first, int last, double *u, BezierCurve bezCurve)
{
int nPts = last - first + 1;
int i;
double *uPrime; /* New parameter values */
uPrime = (double *)malloc(nPts * sizeof(double));
for (i = first; i <= last; i++) {
uPrime[i - first] = NewtonRaphsonRootFind(bezCurve, d[i], u[i - first]);
}
return uPrime;
}
/*
* NewtonRaphsonRootFind:
* Use Newton-Raphson iteration to find better root.
* BezierCurve Q; Current fitted curve
* Vector2 P; Digitized point
* double u; Parameter value for "P"
*/
static double NewtonRaphsonRootFind(BezierCurve Q, Vector2 P, double u)
{
double numerator, denominator;
Vector2 Q1[3], Q2[2]; /* Q' and Q'' */
Vector2 Q_u, Q1_u, Q2_u; /* u evaluated at Q, Q', & Q'' */
double uPrime; /* Improved u */
int i;
/* Compute Q(u) */
Q_u = BezierII(3, Q, u);
/* Generate control vertices for Q' */
for (i = 0; i <= 2; i++) {
Q1[i][0] = (Q[i + 1][0] - Q[i][0]) * 3.0;
Q1[i][1] = (Q[i + 1][1] - Q[i][1]) * 3.0;
}
/* Generate control vertices for Q'' */
for (i = 0; i <= 1; i++) {
Q2[i][0] = (Q1[i + 1][0] - Q1[i][0]) * 2.0;
Q2[i][1] = (Q1[i + 1][1] - Q1[i][1]) * 2.0;
}
/* Compute Q'(u) and Q''(u) */
Q1_u = BezierII(2, Q1, u);
Q2_u = BezierII(1, Q2, u);
/* Compute f(u)/f'(u) */
numerator = (Q_u[0] - P[0]) * (Q1_u[0]) + (Q_u[1] - P[1]) * (Q1_u[1]);
denominator = (Q1_u[0]) * (Q1_u[0]) + (Q1_u[1]) * (Q1_u[1]) + (Q_u[0] - P[0]) * (Q2_u[0]) +
(Q_u[1] - P[1]) * (Q2_u[1]);
/* u = u - f(u)/f'(u) */
if (denominator == 0) { // FIXME
return u;
}
uPrime = u - (numerator / denominator);
return uPrime;
}
/*
* Bezier:
* Evaluate a Bezier curve at a particular parameter value
* int degree; The degree of the bezier curve
* Vector2 *V; Array of control points
* double t; Parametric value to find point for
*/
static Vector2 BezierII(int degree, Vector2 *V, double t)
{
int i, j;
Vector2 Q; /* Point on curve at parameter t */
Vector2 *Vtemp; /* Local copy of control points */
/* Copy array */
Vtemp = (Vector2 *)malloc(uint((degree + 1) * sizeof(Vector2)));
for (i = 0; i <= degree; i++) {
Vtemp[i] = V[i];
}
/* Triangle computation */
for (i = 1; i <= degree; i++) {
for (j = 0; j <= degree - i; j++) {
Vtemp[j][0] = (1.0 - t) * Vtemp[j][0] + t * Vtemp[j + 1][0];
Vtemp[j][1] = (1.0 - t) * Vtemp[j][1] + t * Vtemp[j + 1][1];
}
}
Q = Vtemp[0];
free((void *)Vtemp);
return Q;
}
/*
* B0, B1, B2, B3:
* Bezier multipliers
*/
static double B0(double u)
{
double tmp = 1.0 - u;
return (tmp * tmp * tmp);
}
static double B1(double u)
{
double tmp = 1.0 - u;
return (3 * u * (tmp * tmp));
}
static double B2(double u)
{
double tmp = 1.0 - u;
return (3 * u * u * tmp);
}
static double B3(double u)
{
return (u * u * u);
}
/*
* ComputeLeftTangent, ComputeRightTangent, ComputeCenterTangent:
* Approximate unit tangents at endpoints and "center" of digitized curve
*/
/* Vector2 *d; Digitized points
* int end; Index to "left" end of region
*/
static Vector2 ComputeLeftTangent(Vector2 *d, int end)
{
Vector2 tHat1;
tHat1 = V2SubII(d[end + 1], d[end]);
tHat1 = *V2Normalize(&tHat1);
return tHat1;
}
/* Vector2 *d; Digitized points
* int end; Index to "right" end of region
*/
static Vector2 ComputeRightTangent(Vector2 *d, int end)
{
Vector2 tHat2;
tHat2 = V2SubII(d[end - 1], d[end]);
tHat2 = *V2Normalize(&tHat2);
return tHat2;
}
/* Vector2 *d; Digitized points
* int end; Index to point inside region
*/
static Vector2 ComputeCenterTangent(Vector2 *d, int center)
{
Vector2 V1, V2, tHatCenter;
V1 = V2SubII(d[center - 1], d[center]);
V2 = V2SubII(d[center], d[center + 1]);
tHatCenter[0] = (V1[0] + V2[0]) / 2.0;
tHatCenter[1] = (V1[1] + V2[1]) / 2.0;
tHatCenter = *V2Normalize(&tHatCenter);
/* avoid numerical singularity in the special case when V1 == -V2 */
if (V2Length(&tHatCenter) < M_EPSILON) {
tHatCenter = *V2Normalize(&V1);
}
return tHatCenter;
}
/*
* ChordLengthParameterize:
* Assign parameter values to digitized points using relative distances between points.
* Vector2 *d; Array of digitized points
* int first, last; Indices defining region
*/
static double *ChordLengthParameterize(Vector2 *d, int first, int last)
{
int i;
double *u; /* Parameterization */
u = (double *)malloc(uint(last - first + 1) * sizeof(double));
u[0] = 0.0;
for (i = first + 1; i <= last; i++) {
u[i - first] = u[i - first - 1] + V2DistanceBetween2Points(&d[i], &d[i - 1]);
}
for (i = first + 1; i <= last; i++) {
u[i - first] = u[i - first] / u[last - first];
}
return u;
}
/*
* ComputeMaxError :
* Find the maximum squared distance of digitized points to fitted curve.
* Vector2 *d; Array of digitized points
* int first, last; Indices defining region
* BezierCurve bezCurve; Fitted Bezier curve
* double *u; Parameterization of points
* int *splitPoint; Point of maximum error
*/
static double ComputeMaxError(
Vector2 *d, int first, int last, BezierCurve bezCurve, double *u, int *splitPoint)
{
int i;
double maxDist; /* Maximum error */
double dist; /* Current error */
Vector2 P; /* Point on curve */
Vector2 v; /* Vector from point to curve */
*splitPoint = (last - first + 1) / 2;
maxDist = 0.0;
for (i = first + 1; i < last; i++) {
P = BezierII(3, bezCurve, u[i - first]);
v = V2SubII(P, d[i]);
dist = V2SquaredLength(&v);
if (dist >= maxDist) {
maxDist = dist;
*splitPoint = i;
}
}
return maxDist;
}
static Vector2 V2AddII(Vector2 a, Vector2 b)
{
Vector2 c;
c[0] = a[0] + b[0];
c[1] = a[1] + b[1];
return c;
}
static Vector2 V2ScaleIII(Vector2 v, double s)
{
Vector2 result;
result[0] = v[0] * s;
result[1] = v[1] * s;
return result;
}
static Vector2 V2SubII(Vector2 a, Vector2 b)
{
Vector2 c;
c[0] = a[0] - b[0];
c[1] = a[1] - b[1];
return c;
}
//------------------------- WRAPPER -----------------------------//
FitCurveWrapper::~FitCurveWrapper()
{
_vertices.clear();
}
void FitCurveWrapper::DrawBezierCurve(int n, Vector2 *curve)
{
for (int i = 0; i <= n; ++i) {
_vertices.push_back(curve[i]);
}
}
void FitCurveWrapper::FitCurve(vector<Vec2d> &data, vector<Vec2d> &oCurve, double error)
{
int size = data.size();
Vector2 *d = new Vector2[size];
for (int i = 0; i < size; ++i) {
d[i][0] = data[i][0];
d[i][1] = data[i][1];
}
FitCurve(d, size, error);
delete[] d;
// copy results
for (vector<Vector2>::iterator v = _vertices.begin(), vend = _vertices.end(); v != vend; ++v) {
oCurve.emplace_back(v->x(), v->y());
}
}
void FitCurveWrapper::FitCurve(Vector2 *d, int nPts, double error)
{
Vector2 tHat1, tHat2; /* Unit tangent vectors at endpoints */
tHat1 = ComputeLeftTangent(d, 0);
tHat2 = ComputeRightTangent(d, nPts - 1);
FitCubic(d, 0, nPts - 1, tHat1, tHat2, error);
}
void FitCurveWrapper::FitCubic(
Vector2 *d, int first, int last, Vector2 tHat1, Vector2 tHat2, double error)
{
BezierCurve bezCurve; /* Control points of fitted Bezier curve */
double *u; /* Parameter values for point */
double *uPrime; /* Improved parameter values */
double maxError; /* Maximum fitting error */
int splitPoint; /* Point to split point set at */
int nPts; /* Number of points in subset */
double iterationError; /* Error below which you try iterating */
int maxIterations = 4; /* Max times to try iterating */
Vector2 tHatCenter; /* Unit tangent vector at splitPoint */
int i;
iterationError = error * error;
nPts = last - first + 1;
/* Use heuristic if region only has two points in it */
if (nPts == 2) {
double dist = V2DistanceBetween2Points(&d[last], &d[first]) / 3.0;
bezCurve = (Vector2 *)malloc(4 * sizeof(Vector2));
bezCurve[0] = d[first];
bezCurve[3] = d[last];
V2Add(&bezCurve[0], V2Scale(&tHat1, dist), &bezCurve[1]);
V2Add(&bezCurve[3], V2Scale(&tHat2, dist), &bezCurve[2]);
DrawBezierCurve(3, bezCurve);
free((void *)bezCurve);
return;
}
/* Parameterize points, and attempt to fit curve */
u = ChordLengthParameterize(d, first, last);
bezCurve = GenerateBezier(d, first, last, u, tHat1, tHat2);
/* Find max deviation of points to fitted curve */
maxError = ComputeMaxError(d, first, last, bezCurve, u, &splitPoint);
if (maxError < error) {
DrawBezierCurve(3, bezCurve);
free((void *)u);
free((void *)bezCurve);
return;
}
/* If error not too large, try some reparameterization and iteration */
if (maxError < iterationError) {
for (i = 0; i < maxIterations; i++) {
uPrime = Reparameterize(d, first, last, u, bezCurve);
free((void *)u);
free((void *)bezCurve);
u = uPrime;
bezCurve = GenerateBezier(d, first, last, u, tHat1, tHat2);
maxError = ComputeMaxError(d, first, last, bezCurve, u, &splitPoint);
if (maxError < error) {
DrawBezierCurve(3, bezCurve);
free((void *)u);
free((void *)bezCurve);
return;
}
}
}
/* Fitting failed -- split at max error point and fit recursively */
free((void *)u);
free((void *)bezCurve);
tHatCenter = ComputeCenterTangent(d, splitPoint);
FitCubic(d, first, splitPoint, tHat1, tHatCenter, error);
V2Negate(&tHatCenter);
FitCubic(d, splitPoint, last, tHatCenter, tHat2, error);
}
} /* namespace Freestyle */

View File

@@ -0,0 +1,97 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief An Algorithm for Automatically Fitting Digitized Curves by Philip J. Schneider,
* \brief from "Graphics Gems", Academic Press, 1990
*/
#include <vector>
#include "Geom.h"
#include "../system/FreestyleConfig.h"
namespace Freestyle {
using namespace Geometry;
/* 2d point */
struct Point2 {
double coordinates[2];
Point2()
{
coordinates[0] = 0;
coordinates[1] = 0;
}
inline double operator[](const int i) const
{
return coordinates[i];
}
inline double &operator[](const int i)
{
return coordinates[i];
}
inline double x() const
{
return coordinates[0];
}
inline double y() const
{
return coordinates[1];
}
};
using Vector2 = Point2;
class FitCurveWrapper {
private:
std::vector<Vector2> _vertices;
public:
~FitCurveWrapper();
/** Fits a set of 2D data points to a set of Bezier Curve segments
* data
* Input data points
* oCurve
* Control points of the sets of bezier curve segments.
* Each segment is made of 4 points (polynomial degree of curve = 3)
* error
* max error tolerance between resulting curve and input data
*/
void FitCurve(std::vector<Vec2d> &data, std::vector<Vec2d> &oCurve, double error);
protected:
/* Vec2d *d; Array of digitized points
* int nPts; Number of digitized points
* double error; User-defined error squared
*/
void FitCurve(Vector2 *d, int nPts, double error);
/** Draws a Bezier curve segment
* n
* degree of curve (=3)
* curve
* bezier segments control points
*/
void DrawBezierCurve(int n, Vector2 *curve);
/* Vec2d *d; Array of digitized points
* int first, last; Indices of first and last pts in region
* Vec2d tHat1, tHat2; Unit tangent vectors at endpoints
* double error; User-defined error squared
*/
void FitCubic(Vector2 *d, int first, int last, Vector2 tHat1, Vector2 tHat2, double error);
};
} /* namespace Freestyle */

View File

@@ -0,0 +1,58 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Vectors and Matrices (useful type definitions)
*/
#include "VecMat.h"
#include "../system/Precision.h"
namespace Freestyle {
namespace Geometry {
typedef VecMat::Vec2<uint> Vec2u;
typedef VecMat::Vec2<int> Vec2i;
typedef VecMat::Vec2<float> Vec2f;
typedef VecMat::Vec2<double> Vec2d;
typedef VecMat::Vec2<real> Vec2r;
typedef VecMat::Vec3<uint> Vec3u;
typedef VecMat::Vec3<int> Vec3i;
typedef VecMat::Vec3<float> Vec3f;
typedef VecMat::Vec3<double> Vec3d;
typedef VecMat::Vec3<real> Vec3r;
typedef VecMat::HVec3<uint> HVec3u;
typedef VecMat::HVec3<int> HVec3i;
typedef VecMat::HVec3<float> HVec3f;
typedef VecMat::HVec3<double> HVec3d;
typedef VecMat::HVec3<real> HVec3r;
typedef VecMat::SquareMatrix<uint, 2> Matrix22u;
typedef VecMat::SquareMatrix<int, 2> Matrix22i;
typedef VecMat::SquareMatrix<float, 2> Matrix22f;
typedef VecMat::SquareMatrix<double, 2> Matrix22d;
typedef VecMat::SquareMatrix<real, 2> Matrix22r;
typedef VecMat::SquareMatrix<uint, 3> Matrix33u;
typedef VecMat::SquareMatrix<int, 3> Matrix33i;
typedef VecMat::SquareMatrix<float, 3> Matrix33f;
typedef VecMat::SquareMatrix<double, 3> Matrix33d;
typedef VecMat::SquareMatrix<real, 3> Matrix33r;
typedef VecMat::SquareMatrix<uint, 4> Matrix44u;
typedef VecMat::SquareMatrix<int, 4> Matrix44i;
typedef VecMat::SquareMatrix<float, 4> Matrix44f;
typedef VecMat::SquareMatrix<double, 4> Matrix44d;
typedef VecMat::SquareMatrix<real, 4> Matrix44r;
} // end of namespace Geometry
} /* namespace Freestyle */

View File

@@ -0,0 +1,243 @@
/* SPDX-FileCopyrightText: 2012-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
* \brief Class to define a cleaner of geometry providing a set of useful tools
*/
#if 0
# if defined(__GNUC__) && (__GNUC__ >= 3)
// hash_map is not part of the C++ standard anymore;
// hash_map.h has been kept though for backward compatibility
# include <hash_map.h>
# else
# include <hash_map>
# endif
#endif
#include <cstdio>
#include <list>
#include <map>
#include "GeomCleaner.h"
#include "../system/TimeUtils.h"
#include "BKE_global.hh"
#include "BLI_sys_types.h"
using namespace std;
namespace Freestyle {
void GeomCleaner::SortIndexedVertexArray(const float *iVertices,
uint iVSize,
const uint *iIndices,
uint iISize,
float **oVertices,
uint **oIndices)
{
// First, we build a list of IndexVertex:
list<IndexedVertex> indexedVertices;
uint i;
for (i = 0; i < iVSize; i += 3) {
indexedVertices.emplace_back(Vec3f(iVertices[i], iVertices[i + 1], iVertices[i + 2]), i / 3);
}
// q-sort
indexedVertices.sort();
// build the indices mapping array:
uint *mapIndices = new uint[iVSize / 3];
*oVertices = new float[iVSize];
list<IndexedVertex>::iterator iv;
uint newIndex = 0;
uint vIndex = 0;
for (iv = indexedVertices.begin(); iv != indexedVertices.end(); iv++) {
// Build the final results:
(*oVertices)[vIndex] = iv->x();
(*oVertices)[vIndex + 1] = iv->y();
(*oVertices)[vIndex + 2] = iv->z();
mapIndices[iv->index()] = newIndex;
newIndex++;
vIndex += 3;
}
// Build the final index array:
*oIndices = new uint[iISize];
for (i = 0; i < iISize; i++) {
(*oIndices)[i] = 3 * mapIndices[iIndices[i] / 3];
}
delete[] mapIndices;
}
void GeomCleaner::CompressIndexedVertexArray(const float *iVertices,
uint iVSize,
const uint *iIndices,
uint iISize,
float **oVertices,
uint *oVSize,
uint **oIndices)
{
// First, we build a list of IndexVertex:
vector<Vec3f> vertices;
uint i;
for (i = 0; i < iVSize; i += 3) {
vertices.emplace_back(iVertices[i], iVertices[i + 1], iVertices[i + 2]);
}
uint *mapVertex = new uint[iVSize];
vector<Vec3f>::iterator v = vertices.begin();
vector<Vec3f> compressedVertices;
Vec3f previous = *v;
mapVertex[0] = 0;
compressedVertices.push_back(vertices.front());
v++;
Vec3f current;
i = 1;
for (; v != vertices.end(); v++) {
current = *v;
if (current == previous) {
mapVertex[i] = compressedVertices.size() - 1;
}
else {
compressedVertices.push_back(current);
mapVertex[i] = compressedVertices.size() - 1;
}
previous = current;
i++;
}
// Builds the resulting vertex array:
*oVSize = 3 * compressedVertices.size();
*oVertices = new float[*oVSize];
i = 0;
for (v = compressedVertices.begin(); v != compressedVertices.end(); v++) {
(*oVertices)[i] = (*v)[0];
(*oVertices)[i + 1] = (*v)[1];
(*oVertices)[i + 2] = (*v)[2];
i += 3;
}
// Map the index array:
*oIndices = new uint[iISize];
for (i = 0; i < iISize; i++) {
(*oIndices)[i] = 3 * mapVertex[iIndices[i] / 3];
}
delete[] mapVertex;
}
void GeomCleaner::SortAndCompressIndexedVertexArray(const float *iVertices,
uint iVSize,
const uint *iIndices,
uint iISize,
float **oVertices,
uint *oVSize,
uint **oIndices)
{
// tmp arrays used to store the sorted data:
float *tmpVertices;
uint *tmpIndices;
Chronometer chrono;
// Sort data
chrono.start();
GeomCleaner::SortIndexedVertexArray(
iVertices, iVSize, iIndices, iISize, &tmpVertices, &tmpIndices);
if (blender::G.debug & blender::G_DEBUG_FREESTYLE) {
printf("Sorting: %lf sec.\n", chrono.stop());
}
// compress data
chrono.start();
GeomCleaner::CompressIndexedVertexArray(
tmpVertices, iVSize, tmpIndices, iISize, oVertices, oVSize, oIndices);
real duration = chrono.stop();
if (blender::G.debug & blender::G_DEBUG_FREESTYLE) {
printf("Merging: %lf sec.\n", duration);
}
// deallocates memory:
delete[] tmpVertices;
delete[] tmpIndices;
}
/** Defines a hash table used for searching the Cells */
struct GeomCleanerHasher {
#define _MUL 950706376UL
#define _MOD 2147483647UL
inline size_t operator()(const Vec3r &p) const
{
size_t res = ulong(p[0] * _MUL) % _MOD;
res = (res + ulong(p[1]) * _MUL) % _MOD;
return (res + ulong(p[2]) * _MUL) % _MOD;
}
#undef _MUL
#undef _MOD
};
void GeomCleaner::CleanIndexedVertexArray(const float *iVertices,
uint iVSize,
const uint *iIndices,
uint iISize,
float **oVertices,
uint *oVSize,
uint **oIndices)
{
using cleanHashTable = map<Vec3f, uint>;
vector<Vec3f> vertices;
uint i;
for (i = 0; i < iVSize; i += 3) {
vertices.emplace_back(iVertices[i], iVertices[i + 1], iVertices[i + 2]);
}
cleanHashTable ht;
vector<uint> newIndices;
vector<Vec3f> newVertices;
// elimination of needless points
uint currentIndex = 0;
vector<Vec3f>::const_iterator v = vertices.begin();
vector<Vec3f>::const_iterator end = vertices.end();
cleanHashTable::const_iterator found;
for (; v != end; v++) {
found = ht.find(*v);
if (found != ht.end()) {
// The vertex is already in the new array.
newIndices.push_back((*found).second);
}
else {
newVertices.push_back(*v);
newIndices.push_back(currentIndex);
ht[*v] = currentIndex;
currentIndex++;
}
}
// creation of oVertices array:
*oVSize = 3 * newVertices.size();
*oVertices = new float[*oVSize];
currentIndex = 0;
end = newVertices.end();
for (v = newVertices.begin(); v != end; v++) {
(*oVertices)[currentIndex++] = (*v)[0];
(*oVertices)[currentIndex++] = (*v)[1];
(*oVertices)[currentIndex++] = (*v)[2];
}
// map new indices:
*oIndices = new uint[iISize];
for (i = 0; i < iISize; i++) {
(*oIndices)[i] = 3 * newIndices[iIndices[i] / 3];
}
}
} /* namespace Freestyle */

View File

@@ -0,0 +1,234 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Class to define a cleaner of geometry providing a set of useful tools
*/
#include "Geom.h"
#include "../system/FreestyleConfig.h"
#include "MEM_guardedalloc.h"
namespace Freestyle {
using namespace Geometry;
class GeomCleaner {
public:
inline GeomCleaner() {}
inline ~GeomCleaner() {}
/** Sorts an array of Indexed vertices
* iVertices
* Array of vertices to sort.
* It is organized as a float series of vertex coordinates: XYZXYZXYZ...
* iVSize
* The size of iVertices array.
* iIndices
* The array containing the vertex indices
* (used to refer to the vertex coordinates in an indexed face).
* Each element is a uint multiple of 3.
* iISize
* The size of iIndices array
* oVertices
* Output of sorted vertices.
* A vertex v1 precedes another one v2 in this array
* if v1.x<v2.x, or v1.x=v2.x && v1.y < v2.y or v1.x=v2.y && v1.y=v2.y && v1.z < v2.z.
* The array is organized as a 3-float series giving the vertices coordinates: XYZXYZXYZ...
* oIndices
* Output corresponding to the iIndices array but reorganized in
* order to match the sorted vertex array.
*/
static void SortIndexedVertexArray(const float *iVertices,
uint iVSize,
const uint *iIndices,
uint iISize,
float **oVertices,
uint **oIndices);
/** Compress a SORTED indexed vertex array by eliminating multiple
* appearing occurrences of a single vertex.
* iVertices
* The SORTED vertex array to compress.
* It is organized as a float series of vertex coordinates: XYZXYZXYZ...
* iVSize
* The size of iVertices array.
* iIndices
* The array containing the vertex indices
* (used to refer to the vertex coordinates in an indexed face).
* Each element is a uint multiple of 3.
* iISize
* The size of iIndices array
* oVertices
* The vertex array, result of the compression.
* The array is organized as a 3-float series giving the vertices coordinates: XYZXYZXYZ...
* oVSize
* The size of oVertices.
* oIndices
* The indices array, reorganized to match the compressed oVertices array.
*/
static void CompressIndexedVertexArray(const float *iVertices,
uint iVSize,
const uint *iIndices,
uint iISize,
float **oVertices,
uint *oVSize,
uint **oIndices);
/** Sorts and compress an array of indexed vertices.
* iVertices
* The vertex array to sort then compress. It is organized as a float series of
* vertex coordinates: XYZXYZXYZ...
* iVSize
* The size of iVertices array.
* iIndices
* The array containing the vertex indices
* (used to refer to the vertex coordinates in an indexed face).
* Each element is a uint multiple of 3.
* iISize
* The size of iIndices array
* oVertices
* The vertex array, result of the sorting-compression.
* The array is organized as a 3-float series giving the vertices coordinates: XYZXYZXYZ...
* oVSize
* The size of oVertices.
* oIndices
* The indices array, reorganized to match the sorted and compressed oVertices array.
*/
static void SortAndCompressIndexedVertexArray(const float *iVertices,
uint iVSize,
const uint *iIndices,
uint iISize,
float **oVertices,
uint *oVSize,
uint **oIndices);
/** Cleans an indexed vertex array.
* (Identical to SortAndCompress except that we use here a hash table to create the new array.)
* iVertices
* The vertex array to sort then compress. It is organized as a float series of
* vertex coordinates: XYZXYZXYZ...
* iVSize
* The size of iVertices array.
* iIndices
* The array containing the vertex indices
* (used to refer to the vertex coordinates in an indexed face).
* Each element is a uint multiple of 3.
* iISize
* The size of iIndices array
* oVertices
* The vertex array, result of the sorting-compression.
* The array is organized as a 3-float series giving the vertices coordinates: XYZXYZXYZ...
* oVSize
* The size of oVertices.
* oIndices
* The indices array, reorganized to match the sorted and compressed oVertices array.
*/
static void CleanIndexedVertexArray(const float *iVertices,
uint iVSize,
const uint *iIndices,
uint iISize,
float **oVertices,
uint *oVSize,
uint **oIndices);
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:GeomCleaner")
};
/** Binary operators */
// inline bool operator<(const IndexedVertex& iv1, const IndexedVertex& iv2);
/** Class Indexed Vertex. Used to represent an indexed vertex by storing the vertex coordinates as
* well as its index */
class IndexedVertex {
private:
Vec3f _Vector;
uint _index;
public:
inline IndexedVertex() {}
inline IndexedVertex(Vec3f iVector, uint iIndex)
{
_Vector = iVector;
_index = iIndex;
}
/** accessors */
inline const Vec3f &vector() const
{
return _Vector;
}
inline uint index()
{
return _index;
}
inline float x()
{
return _Vector[0];
}
inline float y()
{
return _Vector[1];
}
inline float z()
{
return _Vector[2];
}
/** modifiers */
inline void setVector(const Vec3f &iVector)
{
_Vector = iVector;
}
inline void setIndex(uint iIndex)
{
_index = iIndex;
}
/** operators */
IndexedVertex &operator=(const IndexedVertex &iv)
{
_Vector = iv._Vector;
_index = iv._index;
return *this;
}
inline float operator[](const uint i)
{
return _Vector[i];
}
// friend inline bool operator<(const IndexedVertex& iv1, const IndexedVertex& iv2);
inline bool operator<(const IndexedVertex &v) const
{
return (_Vector < v._Vector);
}
inline bool operator==(const IndexedVertex &v)
{
return (_Vector == v._Vector);
}
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:IndexedVertex")
};
#if 0
bool operator<(const IndexedVertex &iv1, const IndexedVertex &iv2)
{
return iv1.operator<(iv2);
}
#endif
} /* namespace Freestyle */

View File

@@ -0,0 +1,840 @@
/* SPDX-FileCopyrightText: 2009-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
* \brief Various tools for geometry
*/
#include "GeomUtils.h"
#include "BLI_sys_types.h"
namespace Freestyle::GeomUtils {
// This internal procedure is defined below.
bool intersect2dSegPoly(Vec2r *seg, Vec2r *poly, uint n);
bool intersect2dSeg2dArea(const Vec2r &min, const Vec2r &max, const Vec2r &A, const Vec2r &B)
{
Vec2r seg[2];
seg[0] = A;
seg[1] = B;
Vec2r poly[5];
poly[0][0] = min[0];
poly[0][1] = min[1];
poly[1][0] = max[0];
poly[1][1] = min[1];
poly[2][0] = max[0];
poly[2][1] = max[1];
poly[3][0] = min[0];
poly[3][1] = max[1];
poly[4][0] = min[0];
poly[4][1] = min[1];
return intersect2dSegPoly(seg, poly, 4);
}
bool include2dSeg2dArea(const Vec2r &min, const Vec2r &max, const Vec2r &A, const Vec2r &B)
{
if ((((max[0] > A[0]) && (A[0] > min[0])) && ((max[0] > B[0]) && (B[0] > min[0]))) &&
(((max[1] > A[1]) && (A[1] > min[1])) && ((max[1] > B[1]) && (B[1] > min[1]))))
{
return true;
}
return false;
}
intersection_test intersect2dSeg2dSeg(
const Vec2r &p1, const Vec2r &p2, const Vec2r &p3, const Vec2r &p4, Vec2r &res)
{
real a1, a2, b1, b2, c1, c2; // Coefficients of line eqns
real r1, r2, r3, r4; // 'Sign' values
real denom, num; // Intermediate values
// Compute a1, b1, c1, where line joining points p1 and p2 is "a1 x + b1 y + c1 = 0".
a1 = p2[1] - p1[1];
b1 = p1[0] - p2[0];
c1 = p2[0] * p1[1] - p1[0] * p2[1];
// Compute r3 and r4.
r3 = a1 * p3[0] + b1 * p3[1] + c1;
r4 = a1 * p4[0] + b1 * p4[1] + c1;
// Check signs of r3 and r4. If both point 3 and point 4 lie on same side of line 1,
// the line segments do not intersect.
if (r3 != 0 && r4 != 0 && r3 * r4 > 0.0) {
return DONT_INTERSECT;
}
// Compute a2, b2, c2
a2 = p4[1] - p3[1];
b2 = p3[0] - p4[0];
c2 = p4[0] * p3[1] - p3[0] * p4[1];
// Compute r1 and r2
r1 = a2 * p1[0] + b2 * p1[1] + c2;
r2 = a2 * p2[0] + b2 * p2[1] + c2;
// Check signs of r1 and r2. If both point 1 and point 2 lie on same side of second line
// segment, the line segments do not intersect.
if (r1 != 0 && r2 != 0 && r1 * r2 > 0.0) {
return DONT_INTERSECT;
}
// Line segments intersect: compute intersection point.
denom = a1 * b2 - a2 * b1;
if (fabs(denom) < M_EPSILON) {
return COLINEAR;
}
num = b1 * c2 - b2 * c1;
res[0] = num / denom;
num = a2 * c1 - a1 * c2;
res[1] = num / denom;
return DO_INTERSECT;
}
intersection_test intersect2dLine2dLine(
const Vec2r &p1, const Vec2r &p2, const Vec2r &p3, const Vec2r &p4, Vec2r &res)
{
real a1, a2, b1, b2, c1, c2; // Coefficients of line eqns
real denom, num; // Intermediate values
// Compute a1, b1, c1, where line joining points p1 and p2 is "a1 x + b1 y + c1 = 0".
a1 = p2[1] - p1[1];
b1 = p1[0] - p2[0];
c1 = p2[0] * p1[1] - p1[0] * p2[1];
// Compute a2, b2, c2
a2 = p4[1] - p3[1];
b2 = p3[0] - p4[0];
c2 = p4[0] * p3[1] - p3[0] * p4[1];
// Line segments intersect: compute intersection point.
denom = a1 * b2 - a2 * b1;
if (fabs(denom) < M_EPSILON) {
return COLINEAR;
}
num = b1 * c2 - b2 * c1;
res[0] = num / denom;
num = a2 * c1 - a1 * c2;
res[1] = num / denom;
return DO_INTERSECT;
}
intersection_test intersect2dSeg2dSegParametric(const Vec2r &p1,
const Vec2r &p2,
const Vec2r &p3,
const Vec2r &p4,
real &t,
real &u,
real epsilon)
{
real a1, a2, b1, b2, c1, c2; // Coefficients of line eqns
real r1, r2, r3, r4; // 'Sign' values
real denom, num; // Intermediate values
// Compute a1, b1, c1, where line joining points p1 and p2 is "a1 x + b1 y + c1 = 0".
a1 = p2[1] - p1[1];
b1 = p1[0] - p2[0];
c1 = p2[0] * p1[1] - p1[0] * p2[1];
// Compute r3 and r4.
r3 = a1 * p3[0] + b1 * p3[1] + c1;
r4 = a1 * p4[0] + b1 * p4[1] + c1;
// Check signs of r3 and r4. If both point 3 and point 4 lie on same side of line 1,
// the line segments do not intersect.
if (r3 != 0 && r4 != 0 && r3 * r4 > 0.0) {
return DONT_INTERSECT;
}
// Compute a2, b2, c2
a2 = p4[1] - p3[1];
b2 = p3[0] - p4[0];
c2 = p4[0] * p3[1] - p3[0] * p4[1];
// Compute r1 and r2
r1 = a2 * p1[0] + b2 * p1[1] + c2;
r2 = a2 * p2[0] + b2 * p2[1] + c2;
// Check signs of r1 and r2. If both point 1 and point 2 lie on same side of second line
// segment, the line segments do not intersect.
if (r1 != 0 && r2 != 0 && r1 * r2 > 0.0) {
return DONT_INTERSECT;
}
// Line segments intersect: compute intersection point.
denom = a1 * b2 - a2 * b1;
if (fabs(denom) < epsilon) {
return COLINEAR;
}
real d1, e1;
d1 = p1[1] - p3[1];
e1 = p1[0] - p3[0];
num = -b2 * d1 - a2 * e1;
t = num / denom;
num = -b1 * d1 - a1 * e1;
u = num / denom;
return DO_INTERSECT;
}
// AABB-triangle overlap test code by Tomas Akenine-Möller
// Function: int triBoxOverlap(real boxcenter[3], real boxhalfsize[3],real triverts[3][3]);
// History:
// 2001-03-05: released the code in its first version
// 2001-06-18: changed the order of the tests, faster
//
// Acknowledgement: Many thanks to Pierre Terdiman for suggestions and discussions on how to
// optimize code. Thanks to David Hunt for finding a ">="-bug!
#define X 0
#define Y 1
#define Z 2
#define FINDMINMAX(x0, x1, x2, min, max) \
{ \
min = max = x0; \
if (x1 < min) { \
min = x1; \
} \
if (x1 > max) { \
max = x1; \
} \
if (x2 < min) { \
min = x2; \
} \
if (x2 > max) { \
max = x2; \
} \
} \
(void)0
//======================== X-tests ========================//
#define AXISTEST_X01(a, b, fa, fb) \
{ \
p0 = a * v0[Y] - b * v0[Z]; \
p2 = a * v2[Y] - b * v2[Z]; \
if (p0 < p2) { \
min = p0; \
max = p2; \
} \
else { \
min = p2; \
max = p0; \
} \
rad = fa * boxhalfsize[Y] + fb * boxhalfsize[Z]; \
if (min > rad || max < -rad) { \
return 0; \
} \
} \
(void)0
#define AXISTEST_X2(a, b, fa, fb) \
{ \
p0 = a * v0[Y] - b * v0[Z]; \
p1 = a * v1[Y] - b * v1[Z]; \
if (p0 < p1) { \
min = p0; \
max = p1; \
} \
else { \
min = p1; \
max = p0; \
} \
rad = fa * boxhalfsize[Y] + fb * boxhalfsize[Z]; \
if (min > rad || max < -rad) { \
return 0; \
} \
} \
(void)0
//======================== Y-tests ========================//
#define AXISTEST_Y02(a, b, fa, fb) \
{ \
p0 = -a * v0[X] + b * v0[Z]; \
p2 = -a * v2[X] + b * v2[Z]; \
if (p0 < p2) { \
min = p0; \
max = p2; \
} \
else { \
min = p2; \
max = p0; \
} \
rad = fa * boxhalfsize[X] + fb * boxhalfsize[Z]; \
if (min > rad || max < -rad) { \
return 0; \
} \
} \
(void)0
#define AXISTEST_Y1(a, b, fa, fb) \
{ \
p0 = -a * v0[X] + b * v0[Z]; \
p1 = -a * v1[X] + b * v1[Z]; \
if (p0 < p1) { \
min = p0; \
max = p1; \
} \
else { \
min = p1; \
max = p0; \
} \
rad = fa * boxhalfsize[X] + fb * boxhalfsize[Z]; \
if (min > rad || max < -rad) { \
return 0; \
} \
} \
(void)0
//======================== Z-tests ========================//
#define AXISTEST_Z12(a, b, fa, fb) \
{ \
p1 = a * v1[X] - b * v1[Y]; \
p2 = a * v2[X] - b * v2[Y]; \
if (p2 < p1) { \
min = p2; \
max = p1; \
} \
else { \
min = p1; \
max = p2; \
} \
rad = fa * boxhalfsize[X] + fb * boxhalfsize[Y]; \
if (min > rad || max < -rad) { \
return 0; \
} \
} \
(void)0
#define AXISTEST_Z0(a, b, fa, fb) \
{ \
p0 = a * v0[X] - b * v0[Y]; \
p1 = a * v1[X] - b * v1[Y]; \
if (p0 < p1) { \
min = p0; \
max = p1; \
} \
else { \
min = p1; \
max = p0; \
} \
rad = fa * boxhalfsize[X] + fb * boxhalfsize[Y]; \
if (min > rad || max < -rad) { \
return 0; \
} \
} \
(void)0
// This internal procedure is defined below.
bool overlapPlaneBox(const Vec3r &normal, const real d, const Vec3r &maxbox);
bool overlapTriangleBox(const Vec3r &boxcenter, const Vec3r &boxhalfsize, const Vec3r triverts[3])
{
/* Use separating axis theorem to test overlap between triangle and box need to test for overlap
* in these directions:
*
* 1) The {x,y,z}-directions
* (actually, since we use the AABB of the triangle we do not even need to test these).
* 2) Normal of the triangle.
* 3) `crossproduct(edge from tri, {x,y,z}-directin)` this gives 3x3=9 more tests.
*
* Adapted from Tomas Akenine-Möller code. */
Vec3r v0, v1, v2, normal, e0, e1, e2;
real min, max, d, p0, p1, p2, rad, fex, fey, fez;
// This is the fastest branch on Sun
// move everything so that the boxcenter is in (0, 0, 0)
v0 = triverts[0] - boxcenter;
v1 = triverts[1] - boxcenter;
v2 = triverts[2] - boxcenter;
// compute triangle edges
e0 = v1 - v0;
e1 = v2 - v1;
e2 = v0 - v2;
// Bullet 3:
// Do the 9 tests first (this was faster)
fex = fabs(e0[X]);
fey = fabs(e0[Y]);
fez = fabs(e0[Z]);
AXISTEST_X01(e0[Z], e0[Y], fez, fey);
AXISTEST_Y02(e0[Z], e0[X], fez, fex);
AXISTEST_Z12(e0[Y], e0[X], fey, fex);
fex = fabs(e1[X]);
fey = fabs(e1[Y]);
fez = fabs(e1[Z]);
AXISTEST_X01(e1[Z], e1[Y], fez, fey);
AXISTEST_Y02(e1[Z], e1[X], fez, fex);
AXISTEST_Z0(e1[Y], e1[X], fey, fex);
fex = fabs(e2[X]);
fey = fabs(e2[Y]);
fez = fabs(e2[Z]);
AXISTEST_X2(e2[Z], e2[Y], fez, fey);
AXISTEST_Y1(e2[Z], e2[X], fez, fex);
AXISTEST_Z12(e2[Y], e2[X], fey, fex);
// Bullet 1:
// first test overlap in the {x,y,z}-directions
// find min, max of the triangle each direction, and test for overlap in that direction -- this
// is equivalent to testing a minimal AABB around the triangle against the AABB
// test in X-direction
FINDMINMAX(v0[X], v1[X], v2[X], min, max);
if (min > boxhalfsize[X] || max < -boxhalfsize[X]) {
return false;
}
// test in Y-direction
FINDMINMAX(v0[Y], v1[Y], v2[Y], min, max);
if (min > boxhalfsize[Y] || max < -boxhalfsize[Y]) {
return false;
}
// test in Z-direction
FINDMINMAX(v0[Z], v1[Z], v2[Z], min, max);
if (min > boxhalfsize[Z] || max < -boxhalfsize[Z]) {
return false;
}
// Bullet 2:
// test if the box intersects the plane of the triangle
// compute plane equation of triangle: normal * x + d = 0
normal = e0 ^ e1;
d = -(normal * v0); // plane eq: normal.x + d = 0
if (!overlapPlaneBox(normal, d, boxhalfsize)) {
return false;
}
return true; // box and triangle overlaps
}
bool intersectRayTriangle(const Vec3r &orig,
const Vec3r &dir,
const Vec3r &v0,
const Vec3r &v1,
const Vec3r &v2,
real &t,
real &u,
real &v,
const real epsilon)
{
/* Fast, Minimum Storage Ray-Triangle Intersection.
* Adapted from Tomas Möller and Ben Trumbore code.
*
* Tomas Möller, Prosolvia Clarus AB, Sweden, <tompa@clarus.se>.
* Ben Trumbore, Cornell University, Ithaca, New York <wbt@graphics.cornell.edu>. */
Vec3r edge1, edge2, tvec, pvec, qvec;
real det, inv_det;
// find vectors for two edges sharing v0
edge1 = v1 - v0;
edge2 = v2 - v0;
// begin calculating determinant - also used to calculate U parameter
pvec = dir ^ edge2;
// if determinant is near zero, ray lies in plane of triangle
det = edge1 * pvec;
// calculate distance from v0 to ray origin
tvec = orig - v0;
inv_det = 1.0 / det;
qvec = tvec ^ edge1;
if (det > epsilon) {
u = tvec * pvec;
if (u < 0.0 || u > det) {
return false;
}
// calculate V parameter and test bounds
v = dir * qvec;
if (v < 0.0 || u + v > det) {
return false;
}
}
else if (det < -epsilon) {
// calculate U parameter and test bounds
u = tvec * pvec;
if (u > 0.0 || u < det) {
return false;
}
// calculate V parameter and test bounds
v = dir * qvec;
if (v > 0.0 || u + v < det) {
return false;
}
}
else {
return false; // ray is parallel to the plane of the triangle
}
u *= inv_det;
v *= inv_det;
t = (edge2 * qvec) * inv_det;
return true;
}
intersection_test intersectRayPlane(const Vec3r &orig,
const Vec3r &dir,
const Vec3r &norm,
const real d,
real &t,
const real epsilon)
{
/* Intersection between plane and ray, adapted from Graphics Gems, Didier Badouel
* The plane is represented by a set of points P implicitly defined as `dot(norm, P) + d = 0`.
* The ray is represented as `r(t) = orig + dir * t`. */
real denom = norm * dir;
if (fabs(denom) <= epsilon) { // plane and ray are parallel
if (fabs((norm * orig) + d) <= epsilon) {
return COINCIDENT; // plane and ray are coincident
}
return COLINEAR;
}
t = -(d + (norm * orig)) / denom;
if (t < 0.0f) {
return DONT_INTERSECT;
}
return DO_INTERSECT;
}
bool intersectRayBBox(const Vec3r &orig,
const Vec3r &dir, // ray origin and direction
const Vec3r &boxMin,
const Vec3r &boxMax, // the bbox
real t0,
real t1,
real &tmin, // I0 = orig + tmin * dir is the first intersection
real &tmax, // I1 = orig + tmax * dir is the second intersection
real /*epsilon*/)
{
float tymin, tymax, tzmin, tzmax;
Vec3r inv_direction(1.0 / dir[0], 1.0 / dir[1], 1.0 / dir[2]);
int sign[3];
sign[0] = (inv_direction.x() < 0);
sign[1] = (inv_direction.y() < 0);
sign[2] = (inv_direction.z() < 0);
Vec3r bounds[2];
bounds[0] = boxMin;
bounds[1] = boxMax;
tmin = (bounds[sign[0]].x() - orig.x()) * inv_direction.x();
tmax = (bounds[1 - sign[0]].x() - orig.x()) * inv_direction.x();
tymin = (bounds[sign[1]].y() - orig.y()) * inv_direction.y();
tymax = (bounds[1 - sign[1]].y() - orig.y()) * inv_direction.y();
if ((tmin > tymax) || (tymin > tmax)) {
return false;
}
if (tymin > tmin) {
tmin = tymin;
}
if (tymax < tmax) {
tmax = tymax;
}
tzmin = (bounds[sign[2]].z() - orig.z()) * inv_direction.z();
tzmax = (bounds[1 - sign[2]].z() - orig.z()) * inv_direction.z();
if ((tmin > tzmax) || (tzmin > tmax)) {
return false;
}
if (tzmin > tmin) {
tmin = tzmin;
}
if (tzmax < tmax) {
tmax = tzmax;
}
return ((tmin < t1) && (tmax > t0));
}
// Checks whether 3D points p lies inside or outside of the triangle ABC
bool includePointTriangle(const Vec3r &P, const Vec3r &A, const Vec3r &B, const Vec3r &C)
{
Vec3r AB(B - A);
Vec3r BC(C - B);
Vec3r CA(A - C);
Vec3r AP(P - A);
Vec3r BP(P - B);
Vec3r CP(P - C);
Vec3r N(AB ^ BC); // triangle's normal
N.normalize();
Vec3r J(AB ^ AP), K(BC ^ BP), L(CA ^ CP);
J.normalize();
K.normalize();
L.normalize();
if (J * N < 0) {
return false; // on the right of AB
}
if (K * N < 0) {
return false; // on the right of BC
}
if (L * N < 0) {
return false; // on the right of CA
}
return true;
}
void transformVertex(const Vec3r &vert, const Matrix44r &matrix, Vec3r &res)
{
HVec3r hvert(vert), res_tmp;
real scale;
for (uint j = 0; j < 4; j++) {
scale = hvert[j];
for (uint i = 0; i < 4; i++) {
res_tmp[i] += matrix(i, j) * scale;
}
}
res[0] = res_tmp.x();
res[1] = res_tmp.y();
res[2] = res_tmp.z();
}
void transformVertices(const vector<Vec3r> &vertices, const Matrix44r &trans, vector<Vec3r> &res)
{
size_t i;
res.resize(vertices.size());
for (i = 0; i < vertices.size(); i++) {
transformVertex(vertices[i], trans, res[i]);
}
}
Vec3r rotateVector(const Matrix44r &mat, const Vec3r &v)
{
Vec3r res;
for (uint i = 0; i < 3; i++) {
res[i] = 0;
for (uint j = 0; j < 3; j++) {
res[i] += mat(i, j) * v[j];
}
}
res.normalize();
return res;
}
// This internal procedure is defined below.
void fromCoordAToCoordB(const Vec3r &p, Vec3r &q, const real transform[4][4]);
void fromWorldToCamera(const Vec3r &p, Vec3r &q, const real model_view_matrix[4][4])
{
fromCoordAToCoordB(p, q, model_view_matrix);
}
void fromCameraToRetina(const Vec3r &p, Vec3r &q, const real projection_matrix[4][4])
{
fromCoordAToCoordB(p, q, projection_matrix);
}
void fromRetinaToImage(const Vec3r &p, Vec3r &q, const int viewport[4])
{
// winX:
q[0] = viewport[0] + viewport[2] * (p[0] + 1.0) / 2.0;
// winY:
q[1] = viewport[1] + viewport[3] * (p[1] + 1.0) / 2.0;
// winZ:
q[2] = (p[2] + 1.0) / 2.0;
}
void fromWorldToImage(const Vec3r &p,
Vec3r &q,
const real model_view_matrix[4][4],
const real projection_matrix[4][4],
const int viewport[4])
{
Vec3r p1, p2;
fromWorldToCamera(p, p1, model_view_matrix);
fromCameraToRetina(p1, p2, projection_matrix);
fromRetinaToImage(p2, q, viewport);
q[2] = p1[2];
}
void fromWorldToImage(const Vec3r &p, Vec3r &q, const real transform[4][4], const int viewport[4])
{
fromCoordAToCoordB(p, q, transform);
// winX:
q[0] = viewport[0] + viewport[2] * (q[0] + 1.0) / 2.0;
// winY:
q[1] = viewport[1] + viewport[3] * (q[1] + 1.0) / 2.0;
}
void fromImageToRetina(const Vec3r &p, Vec3r &q, const int viewport[4])
{
q = p;
q[0] = 2.0 * (q[0] - viewport[0]) / viewport[2] - 1.0;
q[1] = 2.0 * (q[1] - viewport[1]) / viewport[3] - 1.0;
}
void fromRetinaToCamera(const Vec3r &p, Vec3r &q, real focal, const real projection_matrix[4][4])
{
if (projection_matrix[3][3] == 0.0) { // perspective
q[0] = (-p[0] * focal) / projection_matrix[0][0];
q[1] = (-p[1] * focal) / projection_matrix[1][1];
q[2] = focal;
}
else { // orthogonal
q[0] = p[0] / projection_matrix[0][0];
q[1] = p[1] / projection_matrix[1][1];
q[2] = focal;
}
}
void fromCameraToWorld(const Vec3r &p, Vec3r &q, const real model_view_matrix[4][4])
{
const real translation[3] = {
model_view_matrix[0][3],
model_view_matrix[1][3],
model_view_matrix[2][3],
};
for (ushort i = 0; i < 3; i++) {
q[i] = 0.0;
for (ushort j = 0; j < 3; j++) {
q[i] += model_view_matrix[j][i] * (p[j] - translation[j]);
}
}
}
//
// Internal code
//
/////////////////////////////////////////////////////////////////////////////
// Copyright 2001, softSurfer (www.softsurfer.com)
// This code may be freely used and modified for any purpose providing that this copyright notice
// is included with it. SoftSurfer makes no warranty for this code, and cannot be held liable for
// any real or imagined damage resulting from its use. Users of this code must verify correctness
// for their application.
#define PERP(u, v) ((u)[0] * (v)[1] - (u)[1] * (v)[0]) // 2D perp product
inline bool intersect2dSegPoly(Vec2r *seg, Vec2r *poly, uint n)
{
if (seg[0] == seg[1]) {
return false;
}
real tE = 0; // the maximum entering segment parameter
real tL = 1; // the minimum leaving segment parameter
real t, N, D; // intersect parameter t = N / D
Vec2r dseg = seg[1] - seg[0]; // the segment direction vector
Vec2r e; // edge vector
for (uint i = 0; i < n; i++) { // process polygon edge poly[i]poly[i+1]
e = poly[i + 1] - poly[i];
N = PERP(e, seg[0] - poly[i]);
D = -PERP(e, dseg);
if (fabs(D) < M_EPSILON) {
if (N < 0) {
return false;
}
continue;
}
t = N / D;
if (D < 0) { // segment seg is entering across this edge
if (t > tE) { // new max tE
tE = t;
if (tE > tL) { // seg enters after leaving polygon
return false;
}
}
}
else { // segment seg is leaving across this edge
if (t < tL) { // new min tL
tL = t;
if (tL < tE) { // seg leaves before entering polygon
return false;
}
}
}
}
// tE <= tL implies that there is a valid intersection subsegment
return true;
}
inline bool overlapPlaneBox(const Vec3r &normal, const real d, const Vec3r &maxbox)
{
Vec3r vmin, vmax;
for (uint q = X; q <= Z; q++) {
if (normal[q] > 0.0f) {
vmin[q] = -maxbox[q];
vmax[q] = maxbox[q];
}
else {
vmin[q] = maxbox[q];
vmax[q] = -maxbox[q];
}
}
if ((normal * vmin) + d > 0.0f) {
return false;
}
if ((normal * vmax) + d >= 0.0f) {
return true;
}
return false;
}
inline void fromCoordAToCoordB(const Vec3r &p, Vec3r &q, const real transform[4][4])
{
HVec3r hp(p);
HVec3r hq(0, 0, 0, 0);
for (uint i = 0; i < 4; i++) {
for (uint j = 0; j < 4; j++) {
hq[i] += transform[i][j] * hp[j];
}
}
if (hq[3] == 0) {
q = p;
return;
}
for (uint k = 0; k < 3; k++) {
q[k] = hq[k] / hq[3];
}
}
} // namespace Freestyle::GeomUtils

View File

@@ -0,0 +1,254 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Various tools for geometry
*/
#include <vector>
#include "Geom.h"
#include "../system/FreestyleConfig.h"
using namespace std;
namespace Freestyle {
using namespace Geometry;
namespace GeomUtils {
//
// Templated procedures
//
/////////////////////////////////////////////////////////////////////////////
/** Computes the distance from a point P to a segment AB */
template<class T> real distPointSegment(const T &P, const T &A, const T &B)
{
T AB, AP, BP;
AB = B - A;
AP = P - A;
BP = P - B;
real c1(AB * AP);
if (c1 <= 0) {
return AP.norm();
}
real c2(AB * AB);
if (c2 <= c1) {
return BP.norm();
}
real b = c1 / c2;
T Pb, PPb;
Pb = A + b * AB;
PPb = P - Pb;
return PPb.norm();
}
//
// Non-templated procedures
//
/////////////////////////////////////////////////////////////////////////////
enum intersection_test {
DONT_INTERSECT,
DO_INTERSECT,
COLINEAR,
COINCIDENT,
};
intersection_test intersect2dSeg2dSeg(const Vec2r &p1,
const Vec2r &p2, // first segment
const Vec2r &p3,
const Vec2r &p4, // second segment
Vec2r &res); // found intersection point
intersection_test intersect2dLine2dLine(const Vec2r &p1,
const Vec2r &p2, // first segment
const Vec2r &p3,
const Vec2r &p4, // second segment
Vec2r &res); // found intersection point
intersection_test intersect2dSeg2dSegParametric(const Vec2r &p1,
const Vec2r &p2, // first segment
const Vec2r &p3,
const Vec2r &p4, // second segment
real &t, // I = P1 + t * P1P2)
real &u, // I = P3 + u * P3P4
real epsilon = M_EPSILON);
/** check whether a 2D segment intersect a 2D region or not */
bool intersect2dSeg2dArea(const Vec2r &min, const Vec2r &max, const Vec2r &A, const Vec2r &B);
/** check whether a 2D segment is included in a 2D region or not */
bool include2dSeg2dArea(const Vec2r &min, const Vec2r &max, const Vec2r &A, const Vec2r &B);
/** Box-triangle overlap test. */
bool overlapTriangleBox(const Vec3r &boxcenter, const Vec3r &boxhalfsize, const Vec3r triverts[3]);
/** Fast, Minimum Storage Ray-Triangle Intersection. */
bool intersectRayTriangle(const Vec3r &orig,
const Vec3r &dir,
const Vec3r &v0,
const Vec3r &v1,
const Vec3r &v2,
real &t, // I = orig + t * dir
real &u,
real &v, // I = (1 - u - v) * v0 + u * v1 + v * v2
const real epsilon = M_EPSILON); // the epsilon to use
/** Intersection between plane and ray. */
intersection_test intersectRayPlane(const Vec3r &orig,
const Vec3r &dir, // ray origin and direction
// plane's normal and offset (plane = { P / P.N + d = 0 })
const Vec3r &norm,
const real d,
real &t, // I = orig + t * dir
const real epsilon = M_EPSILON); // the epsilon to use
/** Intersection Ray-Bounding box (axis aligned).
* Adapted from Williams et al, "An Efficient Robust Ray-Box Intersection Algorithm", JGT 10:1
* (2005), pp. 49-54.
*/
bool intersectRayBBox(const Vec3r &orig,
const Vec3r &dir, // ray origin and direction
const Vec3r &boxMin,
const Vec3r &boxMax, // the bbox
// the interval in which at least on of the intersections must happen
real t0,
real t1,
real &tmin, // Imin = orig + tmin * dir is the first intersection
real &tmax, // Imax = orig + tmax * dir is the second intersection
real epsilon = M_EPSILON); // the epsilon to use
/** Checks whether 3D point P lies inside or outside of the triangle ABC */
bool includePointTriangle(const Vec3r &P, const Vec3r &A, const Vec3r &B, const Vec3r &C);
void transformVertex(const Vec3r &vert, const Matrix44r &matrix, Vec3r &res);
void transformVertices(const vector<Vec3r> &vertices, const Matrix44r &trans, vector<Vec3r> &res);
Vec3r rotateVector(const Matrix44r &mat, const Vec3r &v);
//
// Coordinates systems changing procedures
//
/////////////////////////////////////////////////////////////////////////////
/** From world to image
* p
* point's coordinates expressed in world coordinates system
* q
* vector in which the result will be stored
* model_view_matrix
* The model view matrix expressed in line major order (OpenGL
* matrices are column major ordered)
* projection_matrix
* The projection matrix expressed in line major order (OpenGL
* matrices are column major ordered)
* viewport
* The viewport: x,y coordinates followed by width and height (OpenGL like viewport)
*/
void fromWorldToImage(const Vec3r &p,
Vec3r &q,
const real model_view_matrix[4][4],
const real projection_matrix[4][4],
const int viewport[4]);
/** From world to image
* p
* point's coordinates expressed in world coordinates system
* q
* vector in which the result will be stored
* transform
* The transformation matrix (gathering model view and projection),
* expressed in line major order (OpenGL matrices are column major ordered)
* viewport
* The viewport: x,y coordinates followed by width and height (OpenGL like viewport)
*/
void fromWorldToImage(const Vec3r &p, Vec3r &q, const real transform[4][4], const int viewport[4]);
/** Projects from world coordinates to camera coordinates
* Returns the point's coordinates expressed in the camera's
* coordinates system.
* p
* point's coordinates expressed in world coordinates system
* q
* vector in which the result will be stored
* model_view_matrix
* The model view matrix expressed in line major order (OpenGL
* matrices are column major ordered)
*/
void fromWorldToCamera(const Vec3r &p, Vec3r &q, const real model_view_matrix[4][4]);
/** Projects from World Coordinates to retina coordinates
* Returns the point's coordinates expressed in Retina system.
* p
* point's coordinates expressed in camera system
* q
* vector in which the result will be stored
* projection_matrix
* The projection matrix expressed in line major order (OpenGL
* matrices are column major ordered)
*/
void fromCameraToRetina(const Vec3r &p, Vec3r &q, const real projection_matrix[4][4]);
/** From retina to image.
* Returns the coordinates expressed in Image coordinates system.
* p
* point's coordinates expressed in retina system
* q
* vector in which the result will be stored
* viewport
* The viewport: x,y coordinates followed by width and height (OpenGL like viewport).
*/
void fromRetinaToImage(const Vec3r &p, Vec3r &q, const int viewport[4]);
/** From image to retina
* p
* point's coordinates expressed in image system
* q
* vector in which the result will be stored
* viewport
* The viewport: x,y coordinates followed by width and height (OpenGL like viewport).
*/
void fromImageToRetina(const Vec3r &p, Vec3r &q, const int viewport[4]);
/** computes the coordinates of q in the camera coordinates system,
* using the known z coordinates of the 3D point.
* That means that this method does not inverse any matrices,
* it only computes X and Y from x,y and Z
* p
* point's coordinates expressed in retina system
* q
* vector in which the result will be stored
* projection_matrix
* The projection matrix expressed in line major order (OpenGL
* matrices are column major ordered)
*/
void fromRetinaToCamera(const Vec3r &p, Vec3r &q, real focal, const real projection_matrix[4][4]);
/** Projects from camera coordinates to world coordinates
* Returns the point's coordinates expressed in the world's
* coordinates system.
* p
* point's coordinates expressed in the camera coordinates system
* q
* vector in which the result will be stored
* model_view_matrix
* The model view matrix expressed in line major order (OpenGL
* matrices are column major ordered)
*/
void fromCameraToWorld(const Vec3r &p, Vec3r &q, const real model_view_matrix[4][4]);
} // end of namespace GeomUtils
} /* namespace Freestyle */

View File

@@ -0,0 +1,389 @@
/* SPDX-FileCopyrightText: 2008-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
* \brief Base class to define a cell grid surrounding the bounding box of the scene
*/
#include <stdexcept>
#include "BBox.h"
#include "Grid.h"
#include "BLI_utildefines.h"
namespace Freestyle {
// Grid Visitors
/////////////////
void allOccludersGridVisitor::examineOccluder(Polygon3r *occ)
{
occluders_.push_back(occ);
}
static bool inBox(const Vec3r &inter, const Vec3r &box_min, const Vec3r &box_max)
{
if (((inter.x() >= box_min.x()) && (inter.x() < box_max.x())) &&
((inter.y() >= box_min.y()) && (inter.y() < box_max.y())) &&
((inter.z() >= box_min.z()) && (inter.z() < box_max.z())))
{
return true;
}
return false;
}
void firstIntersectionGridVisitor::examineOccluder(Polygon3r *occ)
{
// check whether the edge and the polygon plane are coincident:
//-------------------------------------------------------------
// first let us compute the plane equation.
Vec3r v1((occ)->getVertices()[0]);
Vec3d normal((occ)->getNormal());
// soc unused - double d = -(v1 * normal);
double tmp_u, tmp_v, tmp_t;
if ((occ)->rayIntersect(ray_org_, ray_dir_, tmp_t, tmp_u, tmp_v)) {
if (fabs(ray_dir_ * normal) > 0.0001) {
// Check whether the intersection is in the cell:
if (inBox(ray_org_ + tmp_t * ray_dir_ / ray_dir_.norm(),
current_cell_->getOrigin(),
current_cell_->getOrigin() + cell_size_))
{
#if 0
Vec3d bboxdiag(_scene3d->bbox().getMax() - _scene3d->bbox().getMin());
if ((t > 1.0e-06 * (min(min(bboxdiag.x(), bboxdiag.y()), bboxdiag.z()))) &&
(t < raylength))
#else
if (tmp_t < t_)
#endif
{
occluder_ = occ;
u_ = tmp_u;
v_ = tmp_v;
t_ = tmp_t;
}
}
else {
occ->userdata2 = nullptr;
}
}
}
} // namespace Freestyle
bool firstIntersectionGridVisitor::stop()
{
if (occluder_) {
return true;
}
return false;
}
// Grid
/////////////////
void Grid::clear()
{
if (!_occluders.empty()) {
for (OccludersSet::iterator it = _occluders.begin(); it != _occluders.end(); it++) {
delete (*it);
}
_occluders.clear();
}
_size = Vec3r(0, 0, 0);
_cell_size = Vec3r(0, 0, 0);
_orig = Vec3r(0, 0, 0);
_cells_nb = Vec3u(0, 0, 0);
//_ray_occluders.clear();
}
void Grid::configure(const Vec3r &orig, const Vec3r &size, uint nb)
{
_orig = orig;
Vec3r tmpSize = size;
// Compute the volume of the desired grid
real grid_vol = size[0] * size[1] * size[2];
if (grid_vol == 0) {
double min = DBL_MAX;
int index = 0;
int nzeros = 0;
for (int i = 0; i < 3; ++i) {
if (size[i] == 0) {
++nzeros;
index = i;
}
if ((size[i] != 0) && (min > size[i])) {
min = size[i];
}
}
if (nzeros > 1) {
throw std::runtime_error("Warning: the 3D grid has more than one null dimension");
}
tmpSize[index] = min;
_orig[index] = _orig[index] - min / 2;
}
// Compute the desired volume of a single cell
real cell_vol = grid_vol / nb;
// The edge of such a cubic cell is cubic root of cellVolume
real edge = pow(cell_vol, 1.0 / 3.0);
// We compute the number of cells par edge such as we cover at least the whole box.
uint i;
for (i = 0; i < 3; i++) {
_cells_nb[i] = uint(floor(tmpSize[i] / edge)) + 1;
}
_size = tmpSize;
for (i = 0; i < 3; i++) {
_cell_size[i] = _size[i] / _cells_nb[i];
}
}
void Grid::insertOccluder(Polygon3r *occluder)
{
const vector<Vec3r> vertices = occluder->getVertices();
if (vertices.empty()) {
return;
}
// add this occluder to the grid's occluders list
addOccluder(occluder);
// find the bbox associated to this polygon
Vec3r min, max;
occluder->getBBox(min, max);
// Retrieve the cell x, y, z coordinates associated with these min and max
Vec3u imax, imin;
getCellCoordinates(max, imax);
getCellCoordinates(min, imin);
// We are now going to fill in the cells overlapping with the polygon bbox.
// If the polygon is a triangle (most of cases), we also check for each of these cells if it is
// overlapping with the triangle in order to only fill in the ones really overlapping the
// triangle.
uint i, x, y, z;
vector<Vec3r>::const_iterator it;
Vec3u coord;
if (vertices.size() == 3) { // Triangle case
Vec3r triverts[3];
i = 0;
for (it = vertices.begin(); it != vertices.end(); it++) {
triverts[i] = Vec3r(*it);
i++;
}
Vec3r boxmin, boxmax;
for (z = imin[2]; z <= imax[2]; z++) {
for (y = imin[1]; y <= imax[1]; y++) {
for (x = imin[0]; x <= imax[0]; x++) {
coord[0] = x;
coord[1] = y;
coord[2] = z;
// We retrieve the box coordinates of the current cell
getCellBox(coord, boxmin, boxmax);
// We check whether the triangle and the box ovewrlap:
Vec3r boxcenter((boxmin + boxmax) / 2.0);
Vec3r boxhalfsize(_cell_size / 2.0);
if (GeomUtils::overlapTriangleBox(boxcenter, boxhalfsize, triverts)) {
// We must then create the Cell and add it to the cells list if it does not exist yet.
// We must then add the occluder to the occluders list of this cell.
Cell *cell = getCell(coord);
if (!cell) {
cell = new Cell(boxmin);
fillCell(coord, *cell);
}
cell->addOccluder(occluder);
}
}
}
}
}
else { // The polygon is not a triangle, we add all the cells overlapping the polygon bbox.
for (z = imin[2]; z <= imax[2]; z++) {
for (y = imin[1]; y <= imax[1]; y++) {
for (x = imin[0]; x <= imax[0]; x++) {
coord[0] = x;
coord[1] = y;
coord[2] = z;
Cell *cell = getCell(coord);
if (!cell) {
Vec3r orig;
getCellOrigin(coord, orig);
cell = new Cell(orig);
fillCell(coord, *cell);
}
cell->addOccluder(occluder);
}
}
}
}
}
bool Grid::nextRayCell(Vec3u &current_cell, Vec3u &next_cell)
{
next_cell = current_cell;
real t_min, t;
uint i;
t_min = FLT_MAX; // init tmin with handle of the case where one or 2 _u[i] = 0.
uint coord = 0; // predominant coord(0=x, 1=y, 2=z)
// using a parametric equation of a line : B = A + t u, we find the tx, ty and tz respectively
// corresponding to the intersections with the plans:
// x = _cell_size[0], y = _cell_size[1], z = _cell_size[2]
for (i = 0; i < 3; i++) {
if (_ray_dir[i] == 0) {
continue;
}
if (_ray_dir[i] > 0) {
t = (_cell_size[i] - _pt[i]) / _ray_dir[i];
}
else {
t = -_pt[i] / _ray_dir[i];
}
if (t < t_min) {
t_min = t;
coord = i;
}
}
// We use the parametric line equation and the found t (tamx) to compute the B coordinates:
Vec3r pt_tmp(_pt);
_pt = pt_tmp + t_min * _ray_dir;
// We express B coordinates in the next cell coordinates system. We just have to
// set the coordinate coord of B to 0 of _CellSize[coord] depending on the sign of _u[coord]
if (_ray_dir[coord] > 0) {
next_cell[coord]++;
_pt[coord] -= _cell_size[coord];
// if we are out of the grid, we must stop
if (next_cell[coord] >= _cells_nb[coord]) {
return false;
}
}
else {
int tmp = next_cell[coord] - 1;
_pt[coord] = _cell_size[coord];
if (tmp < 0) {
return false;
}
next_cell[coord]--;
}
_t += t_min;
if (_t >= _t_end) {
return false;
}
return true;
}
void Grid::castRay(const Vec3r &orig, const Vec3r &end, OccludersSet &occluders, uint timestamp)
{
initRay(orig, end, timestamp);
allOccludersGridVisitor visitor(occluders);
castRayInternal(visitor);
}
void Grid::castInfiniteRay(const Vec3r &orig,
const Vec3r &dir,
OccludersSet &occluders,
uint timestamp)
{
Vec3r end = Vec3r(orig + FLT_MAX * dir / dir.norm());
bool inter = initInfiniteRay(orig, dir, timestamp);
if (!inter) {
return;
}
allOccludersGridVisitor visitor(occluders);
castRayInternal(visitor);
}
Polygon3r *Grid::castRayToFindFirstIntersection(
const Vec3r &orig, const Vec3r &dir, double &t, double &u, double &v, uint timestamp)
{
Polygon3r *occluder = nullptr;
Vec3r end = Vec3r(orig + FLT_MAX * dir / dir.norm());
bool inter = initInfiniteRay(orig, dir, timestamp);
if (!inter) {
return nullptr;
}
firstIntersectionGridVisitor visitor(orig, dir, _cell_size);
castRayInternal(visitor);
// ARB: This doesn't work, because occluders are unordered within any cell
// visitor.occluder() will be an occluder, but we have no guarantee it will be the *first*
// occluder. I assume that is the reason this code is not actually used for FindOccludee.
occluder = visitor.occluder();
t = visitor.t_;
u = visitor.u_;
v = visitor.v_;
return occluder;
}
void Grid::initRay(const Vec3r &orig, const Vec3r &end, uint timestamp)
{
_ray_dir = end - orig;
_t_end = _ray_dir.norm();
_t = 0;
_ray_dir.normalize();
_timestamp = timestamp;
for (uint i = 0; i < 3; i++) {
_current_cell[i] = uint(floor((orig[i] - _orig[i]) / _cell_size[i]));
// soc unused - uint u = _current_cell[i];
_pt[i] = orig[i] - _orig[i] - _current_cell[i] * _cell_size[i];
}
//_ray_occluders.clear();
}
bool Grid::initInfiniteRay(const Vec3r &orig, const Vec3r &dir, uint timestamp)
{
_ray_dir = dir;
_t_end = FLT_MAX;
_t = 0;
_ray_dir.normalize();
_timestamp = timestamp;
// check whether the origin is in or out the box:
Vec3r boxMin(_orig);
Vec3r boxMax(_orig + _size);
BBox<Vec3r> box(boxMin, boxMax);
if (box.inside(orig)) {
for (uint i = 0; i < 3; i++) {
_current_cell[i] = uint(floor((orig[i] - _orig[i]) / _cell_size[i]));
// soc unused - uint u = _current_cell[i];
_pt[i] = orig[i] - _orig[i] - _current_cell[i] * _cell_size[i];
}
}
else {
// is the ray intersecting the box?
real tmin(-1.0), tmax(-1.0);
if (GeomUtils::intersectRayBBox(orig, _ray_dir, boxMin, boxMax, 0, _t_end, tmin, tmax)) {
BLI_assert(tmin != -1.0);
Vec3r newOrig = orig + tmin * _ray_dir;
for (uint i = 0; i < 3; i++) {
_current_cell[i] = uint(floor((newOrig[i] - _orig[i]) / _cell_size[i]));
if (_current_cell[i] == _cells_nb[i]) {
_current_cell[i] = _cells_nb[i] - 1;
}
// soc unused - uint u = _current_cell[i];
_pt[i] = newOrig[i] - _orig[i] - _current_cell[i] * _cell_size[i];
}
}
else {
return false;
}
}
//_ray_occluders.clear();
return true;
}
} /* namespace Freestyle */

View File

@@ -0,0 +1,398 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Base class to define a cell grid surrounding the bounding box of the scene
*/
#include <cstring> // for memset
#include <float.h>
#include <stdint.h> // For POINTER_FROM_UINT, i.e. uintptr_t.
#include <vector>
#include "Geom.h"
#include "GeomUtils.h"
#include "Polygon.h"
#include "../system/FreestyleConfig.h"
#include "BLI_utildefines.h"
#include "MEM_guardedalloc.h"
using namespace std;
namespace Freestyle {
using namespace Geometry;
typedef vector<Polygon3r *> OccludersSet;
//
// Class to define cells used by the regular grid
//
///////////////////////////////////////////////////////////////////////////////
class Cell {
public:
Cell(Vec3r &orig)
{
_orig = orig;
}
virtual ~Cell() {}
inline void addOccluder(Polygon3r *o)
{
if (o) {
_occluders.push_back(o);
}
}
inline const Vec3r &getOrigin()
{
return _orig;
}
inline OccludersSet &getOccluders()
{
return _occluders;
}
private:
Vec3r _orig;
OccludersSet _occluders;
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:Cell")
};
class GridVisitor {
public:
virtual ~GridVisitor() {}; // soc
virtual void discoverCell(Cell * /*cell*/) {}
virtual void examineOccluder(Polygon3r * /*occ*/) {}
virtual void finishCell(Cell * /*cell*/) {}
virtual bool stop()
{
return false;
}
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:GridVisitor")
};
/** Gathers all the occluders belonging to the cells traversed by the ray */
class allOccludersGridVisitor : public GridVisitor {
public:
allOccludersGridVisitor(OccludersSet &occluders) : GridVisitor(), occluders_(occluders) {}
virtual void examineOccluder(Polygon3r *occ);
OccludersSet &occluders()
{
return occluders_;
}
void clear()
{
occluders_.clear();
}
private:
OccludersSet &occluders_;
};
/** Finds the first intersection and breaks.
* The occluder and the intersection information are stored and accessible.
*/
class firstIntersectionGridVisitor : public GridVisitor {
// soc - changed order to remove warnings
public:
double u_, v_, t_;
private:
Polygon3r *occluder_;
Vec3r ray_org_, ray_dir_, cell_size_;
Cell *current_cell_;
public:
firstIntersectionGridVisitor(const Vec3r &ray_org, const Vec3r &ray_dir, const Vec3r &cell_size)
: GridVisitor(),
u_(0),
v_(0),
t_(DBL_MAX),
occluder_(0),
ray_org_(ray_org),
ray_dir_(ray_dir),
cell_size_(cell_size),
current_cell_(0)
{
}
virtual ~firstIntersectionGridVisitor() {}
virtual void discoverCell(Cell *cell)
{
current_cell_ = cell;
}
virtual void examineOccluder(Polygon3r *occ);
virtual bool stop();
Polygon3r *occluder()
{
return occluder_;
}
};
//
// Class to define a regular grid used for ray casting computations
//
///////////////////////////////////////////////////////////////////////////////
class Grid {
public:
/** Builds a Grid. Must be followed by a call to configure() */
Grid() {}
virtual ~Grid()
{
clear();
}
/** clears the grid
* Deletes all the cells, clears the hash-table, resets size, size of cell, number of cells.
*/
virtual void clear();
/** Sets the different parameters of the grid
* orig
* The grid origin
* size
* The grid's dimensions
* nb
* The number of cells of the grid
*/
virtual void configure(const Vec3r &orig, const Vec3r &size, uint nb);
/** returns a vector of integer containing the coordinates of the cell containing the point
* passed as argument
* p
* The point for which we're looking the cell
*/
inline void getCellCoordinates(const Vec3r &p, Vec3u &res)
{
int tmp;
for (int i = 0; i < 3; i++) {
tmp = (int)((p[i] - _orig[i]) / _cell_size[i]);
if (tmp < 0) {
res[i] = 0;
}
else if ((uint)tmp >= _cells_nb[i]) {
res[i] = _cells_nb[i] - 1;
}
else {
res[i] = tmp;
}
}
}
/** Fills the case corresponding to coord with the cell */
virtual void fillCell(const Vec3u &coord, Cell &cell) = 0;
/** returns the cell whose coordinates are passed as argument */
virtual Cell *getCell(const Vec3u &coord) = 0;
/** returns the cell containing the point passed as argument.
* If the cell is empty (contains no occluder), nullptr is returned:
* p
* The point for which we're looking the cell
*/
inline Cell *getCell(const Vec3r &p)
{
Vec3u coord;
getCellCoordinates(p, coord);
return getCell(coord);
}
/** Retrieves the x,y,z coordinates of the origin of the cell whose coordinates (i,j,k)
* is passed as argument:
* cell_coord
* i,j,k integer coordinates for the cell
* orig
* x,y,x vector to be filled in with the cell origin's coordinates
*/
inline void getCellOrigin(const Vec3u &cell_coord, Vec3r &orig)
{
for (uint i = 0; i < 3; i++) {
orig[i] = _orig[i] + cell_coord[i] * _cell_size[i];
}
}
/** Retrieves the box corresponding to the cell whose coordinates are passed as argument:
* cell_coord
* i,j,k integer coordinates for the cell
* min_out
* The min x,y,x vector of the box. Filled in by the method.
* max_out
* The max x,y,z coordinates of the box. Filled in by the method.
*/
inline void getCellBox(const Vec3u &cell_coord, Vec3r &min_out, Vec3r &max_out)
{
getCellOrigin(cell_coord, min_out);
max_out = min_out + _cell_size;
}
/** inserts a convex polygon occluder
* This method is quite coarse insofar as it adds all cells intersecting the polygon bounding
* box convex_poly The list of 3D points constituting a convex polygon
*/
void insertOccluder(Polygon3r *occluder);
/** Adds an occluder to the list of occluders */
void addOccluder(Polygon3r *occluder)
{
_occluders.push_back(occluder);
}
/** Casts a ray between a starting point and an ending point
* Returns the list of occluders contained in the cells intersected by this ray
* Starts with a call to InitRay.
*/
void castRay(const Vec3r &orig, const Vec3r &end, OccludersSet &occluders, uint timestamp);
// Prepares to cast ray without generating OccludersSet
void initAcceleratedRay(const Vec3r &orig, const Vec3r &end, uint timestamp);
/** Casts an infinite ray (still finishing at the end of the grid) from a starting point and in a
* given direction. Returns the list of occluders contained in the cells intersected by this ray
* Starts with a call to InitRay.
*/
void castInfiniteRay(const Vec3r &orig,
const Vec3r &dir,
OccludersSet &occluders,
uint timestamp);
// Prepares to cast ray without generating OccludersSet.
bool initAcceleratedInfiniteRay(const Vec3r &orig, const Vec3r &dir, uint timestamp);
/** Casts an infinite ray (still finishing at the end of the grid) from a starting point and in a
* given direction. Returns the first intersection (occluder,t,u,v) or null. Starts with a call
* to InitRay.
*/
Polygon3r *castRayToFindFirstIntersection(
const Vec3r &orig, const Vec3r &dir, double &t, double &u, double &v, uint timestamp);
/** Init all structures and values for computing the cells intersected by this new ray */
void initRay(const Vec3r &orig, const Vec3r &end, uint timestamp);
/** Init all structures and values for computing the cells intersected by this infinite ray.
* Returns false if the ray doesn't intersect the grid.
*/
bool initInfiniteRay(const Vec3r &orig, const Vec3r &dir, uint timestamp);
/** Accessors */
inline const Vec3r &getOrigin() const
{
return _orig;
}
inline Vec3r gridSize() const
{
return _size;
}
inline Vec3r getCellSize() const
{
return _cell_size;
}
// ARB profiling only:
inline OccludersSet *getOccluders()
{
return &_occluders;
}
void displayDebug()
{
cerr << "Cells nb : " << _cells_nb << endl;
cerr << "Cell size : " << _cell_size << endl;
cerr << "Origin : " << _orig << endl;
cerr << "Occluders nb : " << _occluders.size() << endl;
}
protected:
/** Core of castRay and castInfiniteRay, find occluders along the given ray */
inline void castRayInternal(GridVisitor &visitor)
{
Cell *current_cell = nullptr;
do {
current_cell = getCell(_current_cell);
if (current_cell) {
visitor.discoverCell(current_cell);
OccludersSet &occluders =
current_cell->getOccluders(); // FIXME: I had forgotten the ref &
for (OccludersSet::iterator it = occluders.begin(); it != occluders.end(); it++) {
if (POINTER_AS_UINT((*it)->userdata2) != _timestamp) {
(*it)->userdata2 = POINTER_FROM_UINT(_timestamp);
visitor.examineOccluder(*it);
}
}
visitor.finishCell(current_cell);
}
} while ((!visitor.stop()) && (nextRayCell(_current_cell, _current_cell)));
}
/** Returns the cell next to the cell passed as argument. */
bool nextRayCell(Vec3u &current_cell, Vec3u &next_cell);
uint _timestamp;
Vec3u _cells_nb; // number of cells for x,y,z axis
Vec3r _cell_size; // cell x,y,z dimensions
Vec3r _size; // grid x,y,x dimensions
Vec3r _orig; // grid origin
Vec3r _ray_dir; // direction vector for the ray
Vec3u _current_cell; // The current cell being processed (designated by its 3 coordinates)
Vec3r _pt; // Points corresponding to the incoming and outgoing intersections of one cell with
// the ray
real _t_end; // To know when we are at the end of the ray
real _t;
// OccludersSet _ray_occluders; // Set storing the occluders contained in the cells traversed by
// a ray
OccludersSet _occluders; // List of all occluders inserted in the grid
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:Grid")
};
//
// Class to walk through occluders in grid without building intermediate data structures
//
///////////////////////////////////////////////////////////////////////////////
class VirtualOccludersSet {
public:
VirtualOccludersSet(Grid &_grid) : grid(_grid) {};
Polygon3r *begin();
Polygon3r *next();
Polygon3r *next(bool stopOnNewCell);
private:
Polygon3r *firstOccluderFromNextCell();
Grid &grid;
OccludersSet::iterator it, end;
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:VirtualOccludersSet")
};
} /* namespace Freestyle */

View File

@@ -0,0 +1,35 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
* \brief Class to define a cell grid surrounding the projected image of a scene
*/
#include "GridHelpers.h"
namespace Freestyle {
void GridHelpers::getDefaultViewProscenium(real viewProscenium[4])
{
// Get proscenium boundary for culling
// bufferZone determines the amount by which the area processed should exceed the actual image
// area. This is intended to avoid visible artifacts generated along the proscenium edge. Perhaps
// this is no longer needed now that entire view edges are culled at once, since that
// theoretically should eliminate visible artifacts. To the extent it is still useful, bufferZone
// should be put into the UI as configurable percentage value
const real bufferZone = 0.05;
// borderZone describes a blank border outside the proscenium, but still inside the image area.
// Only intended for exposing possible artifacts along or outside the proscenium edge during
// debugging.
const real borderZone = 0.0;
viewProscenium[0] = blender::g_freestyle.viewport[2] * (borderZone - bufferZone);
viewProscenium[1] = blender::g_freestyle.viewport[2] * (1.0f - borderZone + bufferZone);
viewProscenium[2] = blender::g_freestyle.viewport[3] * (borderZone - bufferZone);
viewProscenium[3] = blender::g_freestyle.viewport[3] * (1.0f - borderZone + bufferZone);
}
GridHelpers::Transform::~Transform() = default;
} /* namespace Freestyle */

View File

@@ -0,0 +1,199 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Class to define a cell grid surrounding the projected image of a scene
*/
#include <vector>
#include "FRS_freestyle.h"
#include "GeomUtils.h"
#include "Polygon.h"
#include "../winged_edge/WEdge.h"
#include "MEM_guardedalloc.h"
namespace Freestyle {
namespace GridHelpers {
/** Computes the distance from a point P to a segment AB */
template<class T> T closestPointToSegment(const T &P, const T &A, const T &B, real &distance)
{
T AB, AP, BP;
AB = B - A;
AP = P - A;
BP = P - B;
real c1(AB * AP);
if (c1 <= 0) {
distance = AP.norm();
return A; // A is closest point
}
real c2(AB * AB);
if (c2 <= c1) {
distance = BP.norm();
return B; // B is closest point
}
real b = c1 / c2;
T Pb, PPb;
Pb = A + b * AB;
PPb = P - Pb;
distance = PPb.norm();
return Pb; // closest point lies on AB
}
inline Vec3r closestPointOnPolygon(const Vec3r &point, const Polygon3r &poly)
{
// First cast a ray from the point onto the polygon plane
// If the ray intersects the polygon, then the intersection point
// is the closest point on the polygon
real t, u, v;
if (poly.rayIntersect(point, poly.getNormal(), t, u, v)) {
return point + poly.getNormal() * t;
}
// Otherwise, get the nearest point on each edge, and take the closest
real distance;
Vec3r closest = closestPointToSegment(
point, poly.getVertices()[2], poly.getVertices()[0], distance);
for (uint i = 0; i < 2; ++i) {
real t;
Vec3r p = closestPointToSegment(point, poly.getVertices()[i], poly.getVertices()[i + 1], t);
if (t < distance) {
distance = t;
closest = p;
}
}
return closest;
}
inline real distancePointToPolygon(const Vec3r &point, const Polygon3r &poly)
{
// First cast a ray from the point onto the polygon plane
// If the ray intersects the polygon, then the intersection point
// is the closest point on the polygon
real t, u, v;
if (poly.rayIntersect(point, poly.getNormal(), t, u, v)) {
return (t > 0.0) ? t : -t;
}
// Otherwise, get the nearest point on each edge, and take the closest
real distance = GeomUtils::distPointSegment(point, poly.getVertices()[2], poly.getVertices()[0]);
for (uint i = 0; i < 2; ++i) {
real t = GeomUtils::distPointSegment(point, poly.getVertices()[i], poly.getVertices()[i + 1]);
if (t < distance) {
distance = t;
}
}
return distance;
}
class Transform {
public:
virtual ~Transform() = 0;
virtual Vec3r operator()(const Vec3r &point) const = 0;
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:GridHelpers:Transform")
};
inline bool insideProscenium(const real proscenium[4], const Polygon3r &polygon)
{
// N.B. The bounding box check is redundant for inserting occluders into cells, because the cell
// selection code in insertOccluders has already guaranteed that the bounding boxes will overlap.
// First check the viewport edges, since they are the easiest case
// Check if the bounding box is entirely outside the proscenium
Vec3r bbMin, bbMax;
polygon.getBBox(bbMin, bbMax);
if (bbMax[0] < proscenium[0] || bbMin[0] > proscenium[1] || bbMax[1] < proscenium[2] ||
bbMin[1] > proscenium[3])
{
return false;
}
Vec3r boxCenter(proscenium[0] + (proscenium[1] - proscenium[0]) / 2.0,
proscenium[2] + (proscenium[3] - proscenium[2]) / 2.0,
0.0);
Vec3r boxHalfSize(
(proscenium[1] - proscenium[0]) / 2.0, (proscenium[3] - proscenium[2]) / 2.0, 1.0);
Vec3r triverts[3] = {
Vec3r(polygon.getVertices()[0][0], polygon.getVertices()[0][1], 0.0),
Vec3r(polygon.getVertices()[1][0], polygon.getVertices()[1][1], 0.0),
Vec3r(polygon.getVertices()[2][0], polygon.getVertices()[2][1], 0.0),
};
return GeomUtils::overlapTriangleBox(boxCenter, boxHalfSize, triverts);
}
inline vector<Vec3r> enumerateVertices(const vector<WOEdge *> &fedges)
{
vector<Vec3r> points;
// Iterate over vertices, storing projections in points
for (vector<WOEdge *>::const_iterator woe = fedges.begin(), woend = fedges.end(); woe != woend;
woe++)
{
points.push_back((*woe)->GetaVertex()->GetVertex());
}
return points;
}
void getDefaultViewProscenium(real viewProscenium[4]);
inline void expandProscenium(real proscenium[4], const Polygon3r &polygon)
{
Vec3r bbMin, bbMax;
polygon.getBBox(bbMin, bbMax);
const real epsilon = 1.0e-6;
if (bbMin[0] <= proscenium[0]) {
proscenium[0] = bbMin[0] - epsilon;
}
if (bbMin[1] <= proscenium[2]) {
proscenium[2] = bbMin[1] - epsilon;
}
if (bbMax[0] >= proscenium[1]) {
proscenium[1] = bbMax[0] + epsilon;
}
if (bbMax[1] >= proscenium[3]) {
proscenium[3] = bbMax[1] + epsilon;
}
}
inline void expandProscenium(real proscenium[4], const Vec3r &point)
{
const real epsilon = 1.0e-6;
if (point[0] <= proscenium[0]) {
proscenium[0] = point[0] - epsilon;
}
if (point[1] <= proscenium[2]) {
proscenium[2] = point[1] - epsilon;
}
if (point[0] >= proscenium[1]) {
proscenium[1] = point[0] + epsilon;
}
if (point[1] >= proscenium[3]) {
proscenium[3] = point[1] + epsilon;
}
}
}; // namespace GridHelpers
} /* namespace Freestyle */

View File

@@ -0,0 +1,34 @@
/* SPDX-FileCopyrightText: 2012-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
* \brief Class to define a cell grid surrounding the bounding box of the scene
*/
#include "HashGrid.h"
#include "BLI_sys_types.h"
namespace Freestyle {
void HashGrid::clear()
{
if (!_cells.empty()) {
for (GridHashTable::iterator it = _cells.begin(); it != _cells.end(); it++) {
Cell *cell = (*it).second;
delete cell;
}
_cells.clear();
}
Grid::clear();
}
void HashGrid::configure(const Vec3r &orig, const Vec3r &size, uint nb)
{
Grid::configure(orig, size, nb);
}
} /* namespace Freestyle */

View File

@@ -0,0 +1,91 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Class to define a cell grid surrounding the bounding box of the scene
*/
#if 0
# if defined(__GNUC__) && (__GNUC__ >= 3)
// hash_map is not part of the C++ standard anymore;
// hash_map.h has been kept though for backward compatibility
# include <hash_map.h>
# else
# include <hash_map>
# endif
#endif
#include <map>
#include "Grid.h"
namespace Freestyle {
/** Defines a hash table used for searching the Cells */
struct GridHasher {
#define _MUL 950706376UL
#define _MOD 2147483647UL
inline size_t operator()(const Vec3u &p) const
{
size_t res = (ulong(p[0] * _MUL)) % _MOD;
res = ((res + ulong(p[1]) * _MUL)) % _MOD;
return ((res + ulong(p[2]) * _MUL)) % _MOD;
}
#undef _MUL
#undef _MOD
};
/** Class to define a regular grid used for ray casting computations */
class HashGrid : public Grid {
public:
typedef map<Vec3u, Cell *> GridHashTable;
HashGrid() : Grid() {}
virtual ~HashGrid()
{
clear();
}
/** clears the grid
* Deletes all the cells, clears the hash-table, resets size, size of cell, number of cells.
*/
virtual void clear();
/** Sets the different parameters of the grid
* orig
* The grid origin
* size
* The grid's dimensions
* nb
* The number of cells of the grid
*/
virtual void configure(const Vec3r &orig, const Vec3r &size, uint nb);
/** returns the cell whose coordinates are passed as argument */
virtual Cell *getCell(const Vec3u &p)
{
Cell *found_cell = nullptr;
GridHashTable::const_iterator found = _cells.find(p);
if (found != _cells.end()) {
found_cell = (*found).second;
}
return found_cell;
}
/** Fills the case p with the cell iCell */
virtual void fillCell(const Vec3u &p, Cell &cell)
{
_cells[p] = &cell;
}
protected:
GridHashTable _cells;
};
} /* namespace Freestyle */

View File

@@ -0,0 +1,271 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
* \brief Class to define Perlin noise
*/
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include "BLI_compiler_attrs.h"
#include "BLI_rand.h"
#include "BLI_sys_types.h"
#include "Noise.h"
namespace Freestyle {
#define SCURVE(a) ((a) * (a) * (3.0 - 2.0 * (a)))
#if 0 // XXX Unused
# define REALSCALE (2.0 / 65536.0)
# define NREALSCALE (2.0 / 4096.0)
# define HASH3D(a, b, c) \
hashTable[hashTable[hashTable[(a) & 0xfff] ^ ((b) & 0xfff)] ^ ((c) & 0xfff)]
# define HASH(a, b, c) (xtab[(xtab[(xtab[(a) & 0xff] ^ (b)) & 0xff] ^ (c)) & 0xff] & 0xff)
# define INCRSUM(m, s, x, y, z) \
((s) * (RTable[m] * 0.5 + RTable[m + 1] * (x) + RTable[m + 2] * (y) + RTable[m + 3] * (z)))
# define MAXSIZE 500
#endif
#define BM 0xff
#define N 0x1000
#if 0 // XXX Unused
# define NP 12 /* 2^N */
# define NM 0xfff
#endif
#define LERP(t, a, b) ((a) + (t) * ((b) - (a)))
#define SETUP(i, b0, b1, r0, r1) \
{ \
(t) = (i) + (N); \
(r0) = modff((t), &(u)); \
(r1) = (r0) - 1.0; \
(b0) = int(u) & BM; \
(b1) = ((b0) + 1) & BM; \
} \
(void)0
static void normalize2(float v[2])
{
float s;
s = sqrt(v[0] * v[0] + v[1] * v[1]);
v[0] = v[0] / s;
v[1] = v[1] / s;
}
static void normalize3(float v[3])
{
float s;
s = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
v[0] = v[0] / s;
v[1] = v[1] / s;
v[2] = v[2] / s;
}
float Noise::turbulence1(float arg, float freq, float amp, uint oct)
{
float t;
float vec;
for (t = 0; oct > 0 && freq > 0; freq *= 2, amp /= 2, --oct) {
vec = freq * arg;
t += smoothNoise1(vec) * amp;
}
return t;
}
float Noise::turbulence2(Vec2f &v, float freq, float amp, uint oct)
{
float t;
Vec2f vec;
for (t = 0; oct > 0 && freq > 0; freq *= 2, amp /= 2, --oct) {
vec.x() = freq * v.x();
vec.y() = freq * v.y();
t += smoothNoise2(vec) * amp;
}
return t;
}
float Noise::turbulence3(Vec3f &v, float freq, float amp, uint oct)
{
float t;
Vec3f vec;
for (t = 0; oct > 0 && freq > 0; freq *= 2, amp /= 2, --oct) {
vec.x() = freq * v.x();
vec.y() = freq * v.y();
vec.z() = freq * v.z();
t += smoothNoise3(vec) * amp;
}
return t;
}
// Noise functions over 1, 2, and 3 dimensions
float Noise::smoothNoise1(float arg)
{
int bx0, bx1;
float rx0, rx1, sx, t, u, v, vec;
vec = arg;
SETUP(vec, bx0, bx1, rx0, rx1);
sx = SCURVE(rx0);
u = rx0 * g1[p[bx0]];
v = rx1 * g1[p[bx1]];
return LERP(sx, u, v);
}
float Noise::smoothNoise2(Vec2f &vec)
{
int bx0, bx1, by0, by1, b00, b10, b01, b11;
float rx0, rx1, ry0, ry1, *q, sx, sy, a, b, t, u, v;
int i, j;
SETUP(vec.x(), bx0, bx1, rx0, rx1);
SETUP(vec.y(), by0, by1, ry0, ry1);
i = p[bx0];
j = p[bx1];
b00 = p[i + by0];
b10 = p[j + by0];
b01 = p[i + by1];
b11 = p[j + by1];
sx = SCURVE(rx0);
sy = SCURVE(ry0);
#define AT2(rx, ry) ((rx) * q[0] + (ry) * q[1])
q = g2[b00];
u = AT2(rx0, ry0);
q = g2[b10];
v = AT2(rx1, ry0);
a = LERP(sx, u, v);
q = g2[b01];
u = AT2(rx0, ry1);
q = g2[b11];
v = AT2(rx1, ry1);
b = LERP(sx, u, v);
#undef AT2
return LERP(sy, a, b);
}
float Noise::smoothNoise3(Vec3f &vec)
{
int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11;
float rx0, rx1, ry0, ry1, rz0, rz1, *q, sy, sz, a, b, c, d, t, u, v;
int i, j;
SETUP(vec.x(), bx0, bx1, rx0, rx1);
SETUP(vec.y(), by0, by1, ry0, ry1);
SETUP(vec.z(), bz0, bz1, rz0, rz1);
i = p[bx0];
j = p[bx1];
b00 = p[i + by0];
b10 = p[j + by0];
b01 = p[i + by1];
b11 = p[j + by1];
t = SCURVE(rx0);
sy = SCURVE(ry0);
sz = SCURVE(rz0);
#define AT3(rx, ry, rz) ((rx) * q[0] + (ry) * q[1] + (rz) * q[2])
q = g3[b00 + bz0];
u = AT3(rx0, ry0, rz0);
q = g3[b10 + bz0];
v = AT3(rx1, ry0, rz0);
a = LERP(t, u, v);
q = g3[b01 + bz0];
u = AT3(rx0, ry1, rz0);
q = g3[b11 + bz0];
v = AT3(rx1, ry1, rz0);
b = LERP(t, u, v);
c = LERP(sy, a, b);
q = g3[b00 + bz1];
u = AT3(rx0, ry0, rz1);
q = g3[b10 + bz1];
v = AT3(rx1, ry0, rz1);
a = LERP(t, u, v);
q = g3[b01 + bz1];
u = AT3(rx0, ry1, rz1);
q = g3[b11 + bz1];
v = AT3(rx1, ry1, rz1);
b = LERP(t, u, v);
d = LERP(sy, a, b);
#undef AT3
return LERP(sz, c, d);
}
Noise::Noise(long seed)
{
/* Use Blender RNG for repeatable results across platforms. */
blender::RNG *rng = blender::BLI_rng_new(seed);
int i, j, k;
for (i = 0; i < _NOISE_B; i++) {
p[i] = i;
g1[i] = float((BLI_rng_get_int(rng) % (_NOISE_B + _NOISE_B)) - _NOISE_B) / _NOISE_B;
for (j = 0; j < 2; j++) {
g2[i][j] = float((BLI_rng_get_int(rng) % (_NOISE_B + _NOISE_B)) - _NOISE_B) / _NOISE_B;
}
normalize2(g2[i]);
for (j = 0; j < 3; j++) {
g3[i][j] = float((BLI_rng_get_int(rng) % (_NOISE_B + _NOISE_B)) - _NOISE_B) / _NOISE_B;
}
normalize3(g3[i]);
}
while (--i) {
k = p[i];
p[i] = p[j = BLI_rng_get_int(rng) % _NOISE_B];
p[j] = k;
}
for (i = 0; i < _NOISE_B + 2; i++) {
p[_NOISE_B + i] = p[i];
g1[_NOISE_B + i] = g1[i];
for (j = 0; j < 2; j++) {
g2[_NOISE_B + i][j] = g2[i][j];
}
for (j = 0; j < 3; j++) {
g3[_NOISE_B + i][j] = g3[i][j];
}
}
BLI_rng_free(rng);
}
} /* namespace Freestyle */

View File

@@ -0,0 +1,64 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Class to define Perlin noise
*/
#include "Geom.h"
#include "../system/FreestyleConfig.h"
#include "MEM_guardedalloc.h"
using namespace std;
namespace Freestyle {
#define _NOISE_B 0x100
using namespace Geometry;
/** Class to provide Perlin Noise functionalities */
class Noise {
public:
/** Builds a Noise object */
Noise(long seed = -1);
/** Destructor */
~Noise() {}
/** Returns a noise value for a 1D element */
float turbulence1(float arg, float freq, float amp, uint oct = 4);
/** Returns a noise value for a 2D element */
float turbulence2(Vec2f &v, float freq, float amp, uint oct = 4);
/** Returns a noise value for a 3D element */
float turbulence3(Vec3f &v, float freq, float amp, uint oct = 4);
/** Returns a smooth noise value for a 1D element */
float smoothNoise1(float arg);
/** Returns a smooth noise value for a 2D element */
float smoothNoise2(Vec2f &vec);
/** Returns a smooth noise value for a 3D element */
float smoothNoise3(Vec3f &vec);
private:
int p[_NOISE_B + _NOISE_B + 2];
float g3[_NOISE_B + _NOISE_B + 2][3];
float g2[_NOISE_B + _NOISE_B + 2][2];
float g1[_NOISE_B + _NOISE_B + 2];
/* UNUSED */
// int start;
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:Noise")
};
} /* namespace Freestyle */

View File

@@ -0,0 +1,208 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Class to define a polygon
*/
#include <vector>
#include "Geom.h"
#include "GeomUtils.h"
#include "MEM_guardedalloc.h"
using namespace std;
namespace Freestyle {
namespace Geometry {
template<class Point> class Polygon {
public:
inline Polygon()
{
_id = 0;
userdata = 0;
userdata2 = 0;
}
inline Polygon(const vector<Point> &vertices)
{
_vertices = vertices;
computeBBox();
_id = 0;
userdata = 0;
userdata2 = 0;
}
inline Polygon(const Polygon<Point> &poly)
{
Point p;
for (typename vector<Point>::const_iterator it = poly.getVertices().begin();
it != poly.getVertices().end();
it++)
{
p = *it;
_vertices.push_back(p);
}
_id = poly.getId();
poly.getBBox(_min, _max);
userdata = 0;
userdata2 = 0;
}
virtual ~Polygon() {}
//
// Accessors
//
/////////////////////////////////////////////////////////////////////////////
inline const vector<Point> &getVertices() const
{
return _vertices;
}
inline void getBBox(Point &min, Point &max) const
{
min = _min;
max = _max;
}
inline Point getBBoxCenter()
{
Point result;
result = (_min + _max) / 2;
return result;
}
inline Point getCenter()
{
Point result;
for (typename vector<Point>::iterator it = _vertices.begin(); it != _vertices.end(); it++) {
result += *it;
}
result /= _vertices.size();
return result;
}
inline uint getId() const
{
return _id;
}
//
// Modifiers
//
/////////////////////////////////////////////////////////////////////////////
inline void setVertices(const vector<Point> &vertices)
{
_vertices.clear();
Point p;
for (typename vector<Point>::const_iterator it = vertices.begin(); it != vertices.end(); it++)
{
p = *it;
_vertices.push_back(p);
}
computeBBox();
}
inline void setId(uint id)
{
_id = id;
}
//
// Other methods
//
/////////////////////////////////////////////////////////////////////////////
inline void computeBBox()
{
if (_vertices.empty()) {
return;
}
_max = _vertices[0];
_min = _vertices[0];
for (typename vector<Point>::iterator it = _vertices.begin(); it != _vertices.end(); it++) {
for (uint i = 0; i < Point::dim(); i++) {
if ((*it)[i] > _max[i]) {
_max[i] = (*it)[i];
}
if ((*it)[i] < _min[i]) {
_min[i] = (*it)[i];
}
}
}
}
// FIXME Is it possible to get rid of userdatas ?
void *userdata;
void *userdata2; // Used during ray casting
protected:
vector<Point> _vertices;
Point _min;
Point _max;
uint _id;
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:Geometry:Polygon")
};
//
// Polygon3r class
//
///////////////////////////////////////////////////////////////////////////////
class Polygon3r : public Polygon<Vec3r> {
public:
inline Polygon3r() : Polygon<Vec3r>() {}
inline Polygon3r(const vector<Vec3r> &vertices, const Vec3r &normal) : Polygon<Vec3r>(vertices)
{
setNormal(normal);
}
inline Polygon3r(const Polygon3r &poly) : Polygon<Vec3r>(poly), _normal(poly._normal) {}
virtual ~Polygon3r() {}
void setNormal(const Vec3r &normal)
{
_normal = normal;
}
inline Vec3r getNormal() const
{
return _normal;
}
/** Check whether the Polygon intersects with the ray or not */
inline bool rayIntersect(const Vec3r &orig,
const Vec3r &dir,
real &t,
real &u,
real &v,
real epsilon = M_EPSILON) const
{
#if 0
if (_vertices.size() < 3) {
return false;
}
#endif
return GeomUtils::intersectRayTriangle(
orig, dir, _vertices[0], _vertices[1], _vertices[2], t, u, v, epsilon);
}
private:
Vec3r _normal;
};
} // end of namespace Geometry
} /* namespace Freestyle */

View File

@@ -0,0 +1,321 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Class to define a Sweep Line
*/
#include <list>
#include <vector>
#include "MEM_guardedalloc.h"
namespace Freestyle {
/** Class to define the intersection between two segments. */
template<class Edge> class Intersection {
public:
template<class EdgeClass> Intersection(EdgeClass *eA, real ta, EdgeClass *eB, real tb)
{
EdgeA = eA;
EdgeB = eB;
tA = ta;
tB = tb;
userdata = 0;
}
Intersection(const Intersection &iBrother)
{
EdgeA = iBrother.EdgeA;
EdgeB = iBrother.EdgeB;
tA = iBrother.tA;
tB = iBrother.tB;
userdata = 0;
}
/** returns the parameter giving the intersection, for the edge iEdge */
real getParameter(Edge *iEdge)
{
if (iEdge == EdgeA) {
return tA;
}
if (iEdge == EdgeB) {
return tB;
}
return 0;
}
public:
void *userdata; // FIXME
Edge *EdgeA; // first segment
Edge *EdgeB; // second segment
real tA; // parameter defining the intersection point with respect to the segment EdgeA.
real tB; // parameter defining the intersection point with respect to the segment EdgeB.
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:Intersection")
};
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable : 4521) // disable warning C4521: multiple copy constructors specified
#endif
template<class T, class Point> class Segment {
public:
Segment() {}
Segment(T &s, const Point &iA, const Point &iB)
{
_edge = s;
if (iA < iB) {
A = iA;
B = iB;
_order = true;
}
else {
A = iB;
B = iA;
_order = false;
}
}
Segment(Segment<T, Point> &iBrother)
{
_edge = iBrother.edge();
A = iBrother.A;
B = iBrother.B;
_Intersections = iBrother._Intersections;
_order = iBrother._order;
}
Segment(const Segment<T, Point> &iBrother)
{
_edge = iBrother._edge;
A = iBrother.A;
B = iBrother.B;
_Intersections = iBrother._Intersections;
_order = iBrother._order;
}
~Segment()
{
_Intersections.clear();
}
inline Point operator[](const ushort &i) const
{
return (i % 2 == 0) ? A : B;
}
inline bool operator==(const Segment<T, Point> &iBrother)
{
if (_edge == iBrother._edge) {
return true;
}
return false;
}
/* Adds an intersection for this segment */
inline void AddIntersection(Intersection<Segment<T, Point>> *i)
{
_Intersections.push_back(i);
}
/** Checks for a common vertex with another edge */
inline bool CommonVertex(const Segment<T, Point> &S, Point &CP)
{
if ((A == S[0]) || (A == S[1])) {
CP = A;
return true;
}
if ((B == S[0]) || (B == S[1])) {
CP = B;
return true;
}
return false;
}
inline vector<Intersection<Segment<T, Point>> *> &intersections()
{
return _Intersections;
}
inline bool order()
{
return _order;
}
inline T &edge()
{
return _edge;
}
private:
T _edge;
Point A;
Point B;
std::vector<Intersection<Segment<T, Point>> *>
_Intersections; // list of intersections parameters
bool _order; // true if A and B are in the same order than _edge.A and _edge.B. false otherwise.
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:Segment")
};
#ifdef _MSC_VER
# pragma warning(pop)
#endif
/** defines a binary function that can be overload by the user to specify at each condition the
* intersection between 2 edges must be computed
*/
template<class T1, class T2> struct binary_rule {
binary_rule() {}
template<class T3, class T4> binary_rule(const binary_rule<T3, T4> & /*brother*/) {}
virtual ~binary_rule() {}
virtual bool operator()(T1 &, T2 &)
{
return true;
}
};
template<class T, class Point> class SweepLine {
public:
SweepLine() {}
~SweepLine()
{
for (typename vector<Intersection<Segment<T, Point>> *>::iterator i = _Intersections.begin(),
iend = _Intersections.end();
i != iend;
i++)
{
delete (*i);
}
}
inline void process(Point &p,
vector<Segment<T, Point> *> &segments,
#if 0
binary_rule<Segment<T, Point>, Segment<T, Point>> &binrule =
binary_rule<Segment<T, Point>, Segment<T, Point>>(),
#else
binary_rule<Segment<T, Point>, Segment<T, Point>> &binrule,
#endif
real epsilon = M_EPSILON)
{
// first we remove the segments that need to be removed and then we add the segments to add
vector<Segment<T, Point> *> toadd;
typename vector<Segment<T, Point> *>::iterator s, send;
for (s = segments.begin(), send = segments.end(); s != send; s++) {
if (p == (*(*s))[0]) {
toadd.push_back((*s));
}
else {
remove((*s));
}
}
for (s = toadd.begin(), send = toadd.end(); s != send; s++) {
add((*s), binrule, epsilon);
}
}
inline void add(Segment<T, Point> *S,
#if 0
binary_rule<Segment<T, Point>, Segment<T, Point>> &binrule =
binary_rule<Segment<T, Point>, Segment<T, Point>>(),
#else
binary_rule<Segment<T, Point>, Segment<T, Point>> &binrule,
#endif
real epsilon)
{
real t, u;
Point CP;
Vec2r v0, v1, v2, v3;
if (true == S->order()) {
v0[0] = ((*S)[0])[0];
v0[1] = ((*S)[0])[1];
v1[0] = ((*S)[1])[0];
v1[1] = ((*S)[1])[1];
}
else {
v1[0] = ((*S)[0])[0];
v1[1] = ((*S)[0])[1];
v0[0] = ((*S)[1])[0];
v0[1] = ((*S)[1])[1];
}
for (typename std::list<Segment<T, Point> *>::iterator s = _set.begin(), send = _set.end();
s != send;
s++)
{
Segment<T, Point> *currentS = (*s);
if (true != binrule(*S, *currentS)) {
continue;
}
if (true == currentS->order()) {
v2[0] = ((*currentS)[0])[0];
v2[1] = ((*currentS)[0])[1];
v3[0] = ((*currentS)[1])[0];
v3[1] = ((*currentS)[1])[1];
}
else {
v3[0] = ((*currentS)[0])[0];
v3[1] = ((*currentS)[0])[1];
v2[0] = ((*currentS)[1])[0];
v2[1] = ((*currentS)[1])[1];
}
if (S->CommonVertex(*currentS, CP)) {
continue; // the two edges have a common vertex->no need to check
}
if (GeomUtils::intersect2dSeg2dSegParametric(v0, v1, v2, v3, t, u, epsilon) ==
GeomUtils::DO_INTERSECT)
{
// create the intersection
Intersection<Segment<T, Point>> *inter = new Intersection<Segment<T, Point>>(
S, t, currentS, u);
// add it to the intersections list
_Intersections.push_back(inter);
// add this intersection to the first edge intersections list
S->AddIntersection(inter);
// add this intersection to the second edge intersections list
currentS->AddIntersection(inter);
}
}
// add the added segment to the list of active segments
_set.push_back(S);
}
inline void remove(Segment<T, Point> *s)
{
if (s->intersections().size() > 0) {
_IntersectedEdges.push_back(s);
}
_set.remove(s);
}
vector<Segment<T, Point> *> &intersectedEdges()
{
return _IntersectedEdges;
}
vector<Intersection<Segment<T, Point>> *> &intersections()
{
return _Intersections;
}
private:
std::list<Segment<T, Point> *>
_set; // set of active edges for a given position of the sweep line
std::vector<Segment<T, Point> *> _IntersectedEdges; // the list of intersected edges
std::vector<Intersection<Segment<T, Point>> *> _Intersections; // the list of all intersections.
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:SweepLine")
};
} /* namespace Freestyle */

View File

@@ -0,0 +1,955 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Vectors and Matrices definition and manipulation
*/
#include <iostream>
#include <math.h>
#include <vector>
#include "MEM_guardedalloc.h"
namespace Freestyle {
namespace VecMat {
namespace Internal {
template<bool B> struct is_false {};
template<> struct is_false<false> {
static inline void ensure() {}
};
} // end of namespace Internal
//
// Vector class
// - T: value type
// - N: dimension
//
/////////////////////////////////////////////////////////////////////////////
template<class T, uint N> class Vec {
public:
typedef T value_type;
// constructors
inline Vec()
{
for (uint i = 0; i < N; i++) {
this->_coord[i] = 0;
}
}
~Vec()
{
Internal::is_false<(N == 0)>::ensure();
}
template<class U> explicit inline Vec(const U tab[N])
{
for (uint i = 0; i < N; i++) {
this->_coord[i] = (T)tab[i];
}
}
template<class U> explicit inline Vec(const std::vector<U> &tab)
{
for (uint i = 0; i < N; i++) {
this->_coord[i] = (T)tab[i];
}
}
template<class U> explicit inline Vec(const Vec<U, N> &v)
{
for (uint i = 0; i < N; i++) {
this->_coord[i] = (T)v[i];
}
}
// accessors
inline value_type operator[](const uint i) const
{
return this->_coord[i];
}
inline value_type &operator[](const uint i)
{
return this->_coord[i];
}
static inline uint dim()
{
return N;
}
// various useful methods
inline value_type norm() const
{
return (T)sqrt((float)squareNorm());
}
inline value_type squareNorm() const
{
return (*this) * (*this);
}
inline Vec<T, N> &normalize()
{
value_type n = norm();
for (uint i = 0; i < N; i++) {
this->_coord[i] /= n;
}
return *this;
}
inline Vec<T, N> &normalizeSafe()
{
value_type n = norm();
if (n) {
for (uint i = 0; i < N; i++) {
this->_coord[i] /= n;
}
}
return *this;
}
// classical operators
inline Vec<T, N> operator+(const Vec<T, N> &v) const
{
Vec<T, N> res(v);
res += *this;
return res;
}
inline Vec<T, N> operator-(const Vec<T, N> &v) const
{
Vec<T, N> res(*this);
res -= v;
return res;
}
inline Vec<T, N> operator*(const typename Vec<T, N>::value_type r) const
{
Vec<T, N> res(*this);
res *= r;
return res;
}
inline Vec<T, N> operator/(const typename Vec<T, N>::value_type r) const
{
Vec<T, N> res(*this);
if (r) {
res /= r;
}
return res;
}
// dot product
inline value_type operator*(const Vec<T, N> &v) const
{
value_type sum = 0;
for (uint i = 0; i < N; i++) {
sum += (*this)[i] * v[i];
}
return sum;
}
template<class U> inline Vec<T, N> &operator=(const Vec<U, N> &v)
{
if (this != &v) {
for (uint i = 0; i < N; i++) {
this->_coord[i] = (T)v[i];
}
}
return *this;
}
template<class U> inline Vec<T, N> &operator+=(const Vec<U, N> &v) &
{
for (uint i = 0; i < N; i++) {
this->_coord[i] += (T)v[i];
}
return *this;
}
template<class U> inline Vec<T, N> &operator-=(const Vec<U, N> &v) &
{
for (uint i = 0; i < N; i++) {
this->_coord[i] -= (T)v[i];
}
return *this;
}
template<class U> inline Vec<T, N> &operator*=(const U r) &
{
for (uint i = 0; i < N; i++) {
this->_coord[i] *= r;
}
return *this;
}
template<class U> inline Vec<T, N> &operator/=(const U r) &
{
if (r) {
for (uint i = 0; i < N; i++) {
this->_coord[i] /= r;
}
}
return *this;
}
inline bool operator==(const Vec<T, N> &v) const
{
for (uint i = 0; i < N; i++) {
if (this->_coord[i] != v[i]) {
return false;
}
}
return true;
}
inline bool operator!=(const Vec<T, N> &v) const
{
for (uint i = 0; i < N; i++) {
if (this->_coord[i] != v[i]) {
return true;
}
}
return false;
}
inline bool operator<(const Vec<T, N> &v) const
{
for (uint i = 0; i < N; i++) {
if (this->_coord[i] < v[i]) {
return true;
}
if (this->_coord[i] > v[i]) {
return false;
}
if (this->_coord[i] == v[i]) {
continue;
}
}
return false;
}
inline bool operator>(const Vec<T, N> &v) const
{
for (uint i = 0; i < N; i++) {
if (this->_coord[i] > v[i]) {
return true;
}
if (this->_coord[i] < v[i]) {
return false;
}
if (this->_coord[i] == v[i]) {
continue;
}
}
return false;
}
protected:
value_type _coord[N];
enum {
_dim = N,
};
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:VecMat:Vec")
};
//
// Vec2 class (2D Vector)
// - T: value type
//
/////////////////////////////////////////////////////////////////////////////
template<class T> class Vec2 : public Vec<T, 2> {
public:
typedef typename Vec<T, 2>::value_type value_type;
inline Vec2() : Vec<T, 2>() {}
template<class U> explicit inline Vec2(const U tab[2]) : Vec<T, 2>(tab) {}
template<class U> explicit inline Vec2(const std::vector<U> &tab) : Vec<T, 2>(tab) {}
template<class U> inline Vec2(const Vec<U, 2> &v) : Vec<T, 2>(v) {}
inline Vec2(const value_type x, const value_type y = 0) : Vec<T, 2>()
{
this->_coord[0] = (T)x;
this->_coord[1] = (T)y;
}
inline value_type x() const
{
return this->_coord[0];
}
inline value_type &x()
{
return this->_coord[0];
}
inline value_type y() const
{
return this->_coord[1];
}
inline value_type &y()
{
return this->_coord[1];
}
inline void setX(const value_type v)
{
this->_coord[0] = v;
}
inline void setY(const value_type v)
{
this->_coord[1] = v;
}
// FIXME: hack swig -- no choice
inline Vec2<T> operator+(const Vec2<T> &v) const
{
Vec2<T> res(v);
res += *this;
return res;
}
inline Vec2<T> operator-(const Vec2<T> &v) const
{
Vec2<T> res(*this);
res -= v;
return res;
}
inline Vec2<T> operator*(const value_type r) const
{
Vec2<T> res(*this);
res *= r;
return res;
}
inline Vec2<T> operator/(const value_type r) const
{
Vec2<T> res(*this);
if (r) {
res /= r;
}
return res;
}
// dot product
inline value_type operator*(const Vec2<T> &v) const
{
value_type sum = 0;
for (uint i = 0; i < 2; i++) {
sum += (*this)[i] * v[i];
}
return sum;
}
};
//
// HVec3 class (3D Vector in homogeneous coordinates)
// - T: value type
//
/////////////////////////////////////////////////////////////////////////////
template<class T> class HVec3 : public Vec<T, 4> {
public:
typedef typename Vec<T, 4>::value_type value_type;
inline HVec3() : Vec<T, 4>() {}
template<class U> explicit inline HVec3(const U tab[4]) : Vec<T, 4>(tab) {}
template<class U> explicit inline HVec3(const std::vector<U> &tab) : Vec<T, 4>(tab) {}
template<class U> inline HVec3(const Vec<U, 4> &v) : Vec<T, 4>(v) {}
inline HVec3(const value_type sx,
const value_type sy = 0,
const value_type sz = 0,
const value_type s = 1)
{
this->_coord[0] = sx;
this->_coord[1] = sy;
this->_coord[2] = sz;
this->_coord[3] = s;
}
template<class U> inline HVec3(const Vec<U, 3> &sv, const U s = 1)
{
this->_coord[0] = (T)sv[0];
this->_coord[1] = (T)sv[1];
this->_coord[2] = (T)sv[2];
this->_coord[3] = (T)s;
}
inline value_type sx() const
{
return this->_coord[0];
}
inline value_type &sx()
{
return this->_coord[0];
}
inline value_type sy() const
{
return this->_coord[1];
}
inline value_type &sy()
{
return this->_coord[1];
}
inline value_type sz() const
{
return this->_coord[2];
}
inline value_type &sz()
{
return this->_coord[2];
}
inline value_type s() const
{
return this->_coord[3];
}
inline value_type &s()
{
return this->_coord[3];
}
// Access to non-homogeneous coordinates in 3D
inline value_type x() const
{
return this->_coord[0] / this->_coord[3];
}
inline value_type y() const
{
return this->_coord[1] / this->_coord[3];
}
inline value_type z() const
{
return this->_coord[2] / this->_coord[3];
}
};
//
// Vec3 class (3D Vec)
// - T: value type
//
/////////////////////////////////////////////////////////////////////////////
template<class T> class Vec3 : public Vec<T, 3> {
public:
typedef typename Vec<T, 3>::value_type value_type;
inline Vec3() : Vec<T, 3>() {}
template<class U> explicit inline Vec3(const U tab[3]) : Vec<T, 3>(tab) {}
template<class U> explicit inline Vec3(const std::vector<U> &tab) : Vec<T, 3>(tab) {}
template<class U> inline Vec3(const Vec<U, 3> &v) : Vec<T, 3>(v) {}
template<class U> inline Vec3(const HVec3<U> &v)
{
this->_coord[0] = (T)v.x();
this->_coord[1] = (T)v.y();
this->_coord[2] = (T)v.z();
}
inline Vec3(const value_type x, const value_type y = 0, const value_type z = 0) : Vec<T, 3>()
{
this->_coord[0] = x;
this->_coord[1] = y;
this->_coord[2] = z;
}
inline value_type x() const
{
return this->_coord[0];
}
inline value_type &x()
{
return this->_coord[0];
}
inline value_type y() const
{
return this->_coord[1];
}
inline value_type &y()
{
return this->_coord[1];
}
inline value_type z() const
{
return this->_coord[2];
}
inline value_type &z()
{
return this->_coord[2];
}
inline void setX(const value_type v)
{
this->_coord[0] = v;
}
inline void setY(const value_type v)
{
this->_coord[1] = v;
}
inline void setZ(const value_type v)
{
this->_coord[2] = v;
}
// classical operators
// FIXME: hack swig -- no choice
inline Vec3<T> operator+(const Vec3<T> &v) const
{
Vec3<T> res(v);
res += *this;
return res;
}
inline Vec3<T> operator-(const Vec3<T> &v) const
{
Vec3<T> res(*this);
res -= v;
return res;
}
inline Vec3<T> operator*(const value_type r) const
{
Vec3<T> res(*this);
res *= r;
return res;
}
inline Vec3<T> operator/(const value_type r) const
{
Vec3<T> res(*this);
if (r) {
res /= r;
}
return res;
}
// dot product
inline value_type operator*(const Vec3<T> &v) const
{
value_type sum = 0;
for (uint i = 0; i < 3; i++) {
sum += (*this)[i] * v[i];
}
return sum;
}
// cross product for 3D Vectors
// FIXME: hack swig -- no choice
inline Vec3<T> operator^(const Vec3<T> &v) const
{
Vec3<T> res((*this)[1] * v[2] - (*this)[2] * v[1],
(*this)[2] * v[0] - (*this)[0] * v[2],
(*this)[0] * v[1] - (*this)[1] * v[0]);
return res;
}
// cross product for 3D Vectors
template<typename U> inline Vec3<T> operator^(const Vec<U, 3> &v) const
{
Vec3<T> res((*this)[1] * v[2] - (*this)[2] * v[1],
(*this)[2] * v[0] - (*this)[0] * v[2],
(*this)[0] * v[1] - (*this)[1] * v[0]);
return res;
}
};
//
// Matrix class
// - T: value type
// - M: rows
// - N: cols
//
/////////////////////////////////////////////////////////////////////////////
// Dirty, but icc under Windows needs this
#define _SIZE (M * N)
template<class T, uint M, uint N> class Matrix {
public:
typedef T value_type;
inline Matrix()
{
for (uint i = 0; i < _SIZE; i++) {
this->_coord[i] = 0;
}
}
~Matrix()
{
Internal::is_false<(M == 0)>::ensure();
Internal::is_false<(N == 0)>::ensure();
}
template<class U> explicit inline Matrix(const U tab[_SIZE])
{
for (uint i = 0; i < _SIZE; i++) {
this->_coord[i] = tab[i];
}
}
template<class U> explicit inline Matrix(const std::vector<U> &tab)
{
for (uint i = 0; i < _SIZE; i++) {
this->_coord[i] = tab[i];
}
}
template<class U> inline Matrix(const Matrix<U, M, N> &m)
{
for (uint i = 0; i < M; i++) {
for (uint j = 0; j < N; j++) {
this->_coord[i * N + j] = (T)m(i, j);
}
}
}
inline value_type operator()(const uint i, const uint j) const
{
return this->_coord[i * N + j];
}
inline value_type &operator()(const uint i, const uint j)
{
return this->_coord[i * N + j];
}
static inline uint rows()
{
return M;
}
static inline uint cols()
{
return N;
}
inline Matrix<T, M, N> &transpose() const
{
Matrix<T, N, M> res;
for (uint i = 0; i < M; i++) {
for (uint j = 0; j < N; j++) {
res(j, i) = this->_coord[i * N + j];
}
}
*this = res;
return *this;
}
template<class U> inline Matrix<T, M, N> &operator=(const Matrix<U, M, N> &m)
{
if (this != &m) {
for (uint i = 0; i < M; i++) {
for (uint j = 0; j < N; j++) {
this->_coord[i * N + j] = (T)m(i, j);
}
}
}
return *this;
}
template<class U> inline Matrix<T, M, N> &operator+=(const Matrix<U, M, N> &m) &
{
for (uint i = 0; i < M; i++) {
for (uint j = 0; j < N; j++) {
this->_coord[i * N + j] += (T)m(i, j);
}
}
return *this;
}
template<class U> inline Matrix<T, M, N> &operator-=(const Matrix<U, M, N> &m) &
{
for (uint i = 0; i < M; i++) {
for (uint j = 0; j < N; j++) {
this->_coord[i * N + j] -= (T)m(i, j);
}
}
return *this;
}
template<class U> inline Matrix<T, M, N> &operator*=(const U lambda) &
{
for (uint i = 0; i < M; i++) {
for (uint j = 0; j < N; j++) {
this->_coord[i * N + j] *= lambda;
}
}
return *this;
}
template<class U> inline Matrix<T, M, N> &operator/=(const U lambda) &
{
if (lambda) {
for (uint i = 0; i < M; i++) {
for (uint j = 0; j < N; j++) {
this->_coord[i * N + j] /= lambda;
}
}
}
return *this;
}
protected:
value_type _coord[_SIZE];
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:VecMat:Matrix")
};
#undef _SIZE
//
// SquareMatrix class
// - T: value type
// - N: rows & cols
//
/////////////////////////////////////////////////////////////////////////////
// Dirty, but icc under Windows needs this
#define _SIZE (N * N)
template<class T, uint N> class SquareMatrix : public Matrix<T, N, N> {
public:
typedef T value_type;
inline SquareMatrix() : Matrix<T, N, N>() {}
template<class U> explicit inline SquareMatrix(const U tab[_SIZE]) : Matrix<T, N, N>(tab) {}
template<class U> explicit inline SquareMatrix(const std::vector<U> &tab) : Matrix<T, N, N>(tab)
{
}
template<class U> inline SquareMatrix(const Matrix<U, N, N> &m) : Matrix<T, N, N>(m) {}
static inline SquareMatrix<T, N> identity()
{
SquareMatrix<T, N> res;
for (uint i = 0; i < N; i++) {
res(i, i) = 1;
}
return res;
}
};
#undef _SIZE
//
// Vector external functions
//
/////////////////////////////////////////////////////////////////////////////
#if 0
template<class T, uint N> inline Vec<T, N> operator+(const Vec<T, N> &v1, const Vec<T, N> &v2)
{
Vec<T, N> res(v1);
res += v2;
return res;
}
template<class T, uint N> inline Vec<T, N> operator-(const Vec<T, N> &v1, const Vec<T, N> &v2)
{
Vec<T, N> res(v1);
res -= v2;
return res;
}
template<class T, uint N>
inline Vec<T, N> operator*(const Vec<T, N> &v, const typename Vec<T, N>::value_type r)
{
Vec<T, N> res(v);
res *= r;
return res;
}
#endif
template<class T, uint N>
inline Vec<T, N> operator*(const typename Vec<T, N>::value_type r, const Vec<T, N> &v)
{
Vec<T, N> res(v);
res *= r;
return res;
}
#if 0
template<class T, uint N>
inline Vec<T, N> operator/(const Vec<T, N> &v, const typename Vec<T, N>::value_type r)
{
Vec<T, N> res(v);
if (r) {
res /= r;
}
return res;
}
// dot product
template<class T, uint N>
inline typename Vec<T, N>::value_type operator*(const Vec<T, N> &v1, const Vec<T, N> &v2)
{
typename Vec<T, N>::value_type sum = 0;
for (uint i = 0; i < N; i++) {
sum += v1[i] * v2[i];
}
return sum;
}
// cross product for 3D Vectors
template<typename T> inline Vec3<T> operator^(const Vec<T, 3> &v1, const Vec<T, 3> &v2)
{
Vec3<T> res(
v1[1] * v2[2] - v1[2] * v2[1], v1[2] * v2[0] - v1[0] * v2[2], v1[0] * v2[1] - v1[1] * v2[0]);
return res;
}
#endif
// stream operator
template<class T, uint N> inline std::ostream &operator<<(std::ostream &s, const Vec<T, N> &v)
{
uint i;
s << "[";
for (i = 0; i < N - 1; i++) {
s << v[i] << ", ";
}
s << v[i] << "]";
return s;
}
//
// Matrix external functions
//
/////////////////////////////////////////////////////////////////////////////
template<class T, uint M, uint N>
inline Matrix<T, M, N> operator+(const Matrix<T, M, N> &m1, const Matrix<T, M, N> &m2)
{
Matrix<T, M, N> res(m1);
res += m2;
return res;
}
template<class T, uint M, uint N>
inline Matrix<T, M, N> operator-(const Matrix<T, M, N> &m1, const Matrix<T, M, N> &m2)
{
Matrix<T, M, N> res(m1);
res -= m2;
return res;
}
template<class T, uint M, uint N>
inline Matrix<T, M, N> operator*(const Matrix<T, M, N> &m1,
const typename Matrix<T, M, N>::value_type lambda)
{
Matrix<T, M, N> res(m1);
res *= lambda;
return res;
}
template<class T, uint M, uint N>
inline Matrix<T, M, N> operator*(const typename Matrix<T, M, N>::value_type lambda,
const Matrix<T, M, N> &m1)
{
Matrix<T, M, N> res(m1);
res *= lambda;
return res;
}
template<class T, uint M, uint N>
inline Matrix<T, M, N> operator/(const Matrix<T, M, N> &m1,
const typename Matrix<T, M, N>::value_type lambda)
{
Matrix<T, M, N> res(m1);
res /= lambda;
return res;
}
template<class T, uint M, uint N, uint P>
inline Matrix<T, M, P> operator*(const Matrix<T, M, N> &m1, const Matrix<T, N, P> &m2)
{
uint i, j, k;
Matrix<T, M, P> res;
typename Matrix<T, N, P>::value_type scale;
for (j = 0; j < P; j++) {
for (k = 0; k < N; k++) {
scale = m2(k, j);
for (i = 0; i < N; i++) {
res(i, j) += m1(i, k) * scale;
}
}
}
return res;
}
template<class T, uint M, uint N>
inline Vec<T, M> operator*(const Matrix<T, M, N> &m, const Vec<T, N> &v)
{
Vec<T, M> res;
typename Matrix<T, M, N>::value_type scale;
for (uint j = 0; j < M; j++) {
scale = v[j];
for (uint i = 0; i < N; i++) {
res[i] += m(i, j) * scale;
}
}
return res;
}
// stream operator
template<class T, uint M, uint N>
inline std::ostream &operator<<(std::ostream &s, const Matrix<T, M, N> &m)
{
uint i, j;
for (i = 0; i < M; i++) {
s << "[";
for (j = 0; j < N - 1; j++) {
s << m(i, j) << ", ";
}
s << m(i, j) << "]" << std::endl;
}
return s;
}
} // end of namespace VecMat
} /* namespace Freestyle */

View File

@@ -0,0 +1,233 @@
/* SPDX-FileCopyrightText: 2000 `Bruno Levy <levy@loria.fr>`
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* The Original Code is:
* - GXML/Graphite: Geometry and Graphics Programming Library + Utilities.
*/
/** \file
* \ingroup freestyle
*/
#include "matrix_util.h"
#include "BLI_math_base.h"
#include "BLI_utildefines.h"
namespace Freestyle::OGF::MatrixUtil {
static const double EPS = 0.00001;
static int MAX_ITER = 100;
void semi_definite_symmetric_eigen(const double *mat, int n, double *eigen_vec, double *eigen_val)
{
double *a, *v;
double a_norm, a_normEPS, thr, thr_nn;
int nb_iter = 0;
int jj;
int i, j, k, ij, ik, l, m, lm, mq, lq, ll, mm, imv, im, iq, ilv, il, nn;
int *index;
double a_ij, a_lm, a_ll, a_mm, a_im, a_il;
double a_lm_2;
double v_ilv, v_imv;
double x;
double sinx, sinx_2, cosx, cosx_2, sincos;
double delta;
// Number of entries in mat
nn = (n * (n + 1)) / 2;
// Step 1: Copy mat to a
a = new double[nn];
for (ij = 0; ij < nn; ij++) {
a[ij] = mat[ij];
}
// Ugly Fortran-porting trick: indices for a are between 1 and n
a--;
// Step 2 : Init diagonalization matrix as the unit matrix
v = new double[n * n];
ij = 0;
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
if (i == j) {
v[ij++] = 1.0;
}
else {
v[ij++] = 0.0;
}
}
}
// Ugly Fortran-porting trick: indices for v are between 1 and n
v--;
// Step 3 : compute the weight of the non diagonal terms
ij = 1;
a_norm = 0.0;
for (i = 1; i <= n; i++) {
for (j = 1; j <= i; j++) {
if (i != j) {
a_ij = a[ij];
a_norm += a_ij * a_ij;
}
ij++;
}
}
if (a_norm != 0.0) {
a_normEPS = a_norm * EPS;
thr = a_norm;
// Step 4 : rotations
while (thr > a_normEPS && nb_iter < MAX_ITER) {
nb_iter++;
thr_nn = thr / nn;
for (l = 1; l < n; l++) {
for (m = l + 1; m <= n; m++) {
// compute sinx and cosx
lq = (l * l - l) / 2;
mq = (m * m - m) / 2;
lm = l + mq;
a_lm = a[lm];
a_lm_2 = a_lm * a_lm;
if (a_lm_2 < thr_nn) {
continue;
}
ll = l + lq;
mm = m + mq;
a_ll = a[ll];
a_mm = a[mm];
delta = a_ll - a_mm;
if (delta == 0.0) {
x = -M_PI_4;
}
else {
x = -atan((a_lm + a_lm) / delta) / 2.0;
}
sinx = sin(x);
cosx = cos(x);
sinx_2 = sinx * sinx;
cosx_2 = cosx * cosx;
sincos = sinx * cosx;
// rotate L and M columns
ilv = n * (l - 1);
imv = n * (m - 1);
for (i = 1; i <= n; i++) {
if (!ELEM(i, l, m)) {
iq = (i * i - i) / 2;
if (i < m) {
im = i + mq;
}
else {
im = m + iq;
}
a_im = a[im];
if (i < l) {
il = i + lq;
}
else {
il = l + iq;
}
a_il = a[il];
a[il] = a_il * cosx - a_im * sinx;
a[im] = a_il * sinx + a_im * cosx;
}
ilv++;
imv++;
v_ilv = v[ilv];
v_imv = v[imv];
v[ilv] = cosx * v_ilv - sinx * v_imv;
v[imv] = sinx * v_ilv + cosx * v_imv;
}
x = a_lm * sincos;
x += x;
a[ll] = a_ll * cosx_2 + a_mm * sinx_2 - x;
a[mm] = a_ll * sinx_2 + a_mm * cosx_2 + x;
a[lm] = 0.0;
thr = fabs(thr - a_lm_2);
}
}
}
}
// Step 5: index conversion and copy eigen values
// back from Fortran to C++
a++;
for (i = 0; i < n; i++) {
k = i + (i * (i + 1)) / 2;
eigen_val[i] = a[k];
}
delete[] a;
// Step 6: sort the eigen values and eigen vectors
index = new int[n];
for (i = 0; i < n; i++) {
index[i] = i;
}
for (i = 0; i < (n - 1); i++) {
x = eigen_val[i];
k = i;
for (j = i + 1; j < n; j++) {
if (x < eigen_val[j]) {
k = j;
x = eigen_val[j];
}
}
eigen_val[k] = eigen_val[i];
eigen_val[i] = x;
jj = index[k];
index[k] = index[i];
index[i] = jj;
}
// Step 7: save the eigen vectors
// back from Fortran to C++
v++;
ij = 0;
for (k = 0; k < n; k++) {
ik = index[k] * n;
for (i = 0; i < n; i++) {
eigen_vec[ij++] = v[ik++];
}
}
delete[] v;
delete[] index;
}
//_________________________________________________________
} // namespace Freestyle::OGF::MatrixUtil

View File

@@ -0,0 +1,46 @@
/* SPDX-FileCopyrightText: 2000 `Bruno Levy <levy@loria.fr>`
* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* The Original Code is:
* - GXML/Graphite: Geometry and Graphics Programming Library + Utilities.
*/
#pragma once
/** \file
* \ingroup freestyle
*/
#include "../system/FreestyleConfig.h"
namespace Freestyle {
namespace OGF {
namespace MatrixUtil {
/**
* computes the eigen values and eigen vectors of a semi definite symmetric matrix
*
* \param mat: The matrix stored in column symmetric storage, i.e.
* <pre>
* matrix = { m11, m12, m22, m13, m23, m33, m14, m24, m34, m44 ... }
* size = n(n+1)/2
* </pre>
*
* \param eigen_vec: (return) = { v1, v2, v3, ..., vn }
* where `vk = vk0, vk1, ..., vkn`
* `size = n^2`, must be allocated by caller.
*
* \param eigen_val: (return) are in decreasing order
* `size = n`, must be allocated by caller.
*/
void semi_definite_symmetric_eigen(const double *mat, int n, double *eigen_vec, double *eigen_val);
} // namespace MatrixUtil
} // namespace OGF
} /* namespace Freestyle */

View File

@@ -0,0 +1,66 @@
/* SPDX-FileCopyrightText: 2000 `Bruno Levy <levy@loria.fr>`
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* The Original Code is:
* - OGF/Graphite: Geometry and Graphics Programming Library + Utilities.
*/
/** \file
* \ingroup freestyle
*/
#include "normal_cycle.h"
#include "matrix_util.h"
namespace Freestyle::OGF {
//_________________________________________________________
void NormalCycle::begin()
{
M_[0] = M_[1] = M_[2] = M_[3] = M_[4] = M_[5] = 0;
}
void NormalCycle::end()
{
double eigen_vectors[9];
MatrixUtil::semi_definite_symmetric_eigen(M_, 3, eigen_vectors, eigen_value_);
axis_[0] = Vec3r(eigen_vectors[0], eigen_vectors[1], eigen_vectors[2]);
axis_[1] = Vec3r(eigen_vectors[3], eigen_vectors[4], eigen_vectors[5]);
axis_[2] = Vec3r(eigen_vectors[6], eigen_vectors[7], eigen_vectors[8]);
// Normalize the eigen vectors
for (int i = 0; i < 3; i++) {
axis_[i].normalize();
}
// Sort the eigen vectors
i_[0] = 0;
i_[1] = 1;
i_[2] = 2;
double l0 = ::fabs(eigen_value_[0]);
double l1 = ::fabs(eigen_value_[1]);
double l2 = ::fabs(eigen_value_[2]);
if (l1 > l0) {
ogf_swap(l0, l1);
ogf_swap(i_[0], i_[1]);
}
if (l2 > l1) {
ogf_swap(l1, l2);
ogf_swap(i_[1], i_[2]);
}
if (l1 > l0) {
ogf_swap(l0, l1);
ogf_swap(i_[0], i_[1]);
}
}
//_________________________________________________________
} // namespace Freestyle::OGF

View File

@@ -0,0 +1,123 @@
/* SPDX-FileCopyrightText: 2000 `Bruno Levy <levy@loria.fr>`
* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* The Original Code is:
* - OGF/Graphite: Geometry and Graphics Programming Library + Utilities.
*/
#pragma once
/** \file
* \ingroup freestyle
*/
#include "Geom.h"
#include "../system/FreestyleConfig.h"
#include "MEM_guardedalloc.h"
namespace Freestyle {
using namespace Geometry;
namespace OGF {
template<class T> inline void ogf_swap(T &x, T &y)
{
T z = x;
x = y;
y = z;
}
//_________________________________________________________
/**
* NormalCycle evaluates the curvature tensor in function
* of a set of dihedral angles and associated vectors.
* Reference:
* Restricted Delaunay Triangulation and Normal Cycle,
* D. Cohen-Steiner and J.M. Morvan,
* SOCG 2003
*/
class NormalCycle {
public:
void begin();
void end();
/**
* NOTE: the specified edge vector needs to be pre-clipped by the neighborhood.
*/
void accumulate_dihedral_angle(const Vec3r &edge, real angle, real neigh_area = 1.0);
const Vec3r &eigen_vector(int i) const
{
return axis_[i_[i]];
}
real eigen_value(int i) const
{
return eigen_value_[i_[i]];
}
const Vec3r &N() const
{
return eigen_vector(2);
}
const Vec3r &Kmax() const
{
return eigen_vector(1);
}
const Vec3r &Kmin() const
{
return eigen_vector(0);
}
real n() const
{
return eigen_value(2);
}
real kmax() const
{
return eigen_value(1);
}
real kmin() const
{
return eigen_value(0);
}
private:
/* UNUSED */
// real center_[3];
Vec3r axis_[3];
real eigen_value_[3];
real M_[6];
int i_[3];
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:OGF:NormalCycle")
};
inline void NormalCycle::accumulate_dihedral_angle(const Vec3r &edge,
const double beta,
double neigh_area)
{
double s = beta * neigh_area / edge.norm();
M_[0] += s * edge.x() * edge.x();
M_[1] += s * edge.x() * edge.y();
M_[2] += s * edge.y() * edge.y();
M_[3] += s * edge.x() * edge.z();
M_[4] += s * edge.y() * edge.z();
M_[5] += s * edge.z() * edge.z();
}
//_________________________________________________________
} // namespace OGF
} /* namespace Freestyle */

View File

@@ -0,0 +1,89 @@
/* SPDX-FileCopyrightText: 2008-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
* \brief Class to perform gaussian filtering operations on an image
*/
#include <cstdlib>
#include "GaussianFilter.h"
#include "BLI_math_base.h"
namespace Freestyle {
GaussianFilter::GaussianFilter(float iSigma)
{
_sigma = iSigma;
_mask = nullptr;
computeMask();
}
GaussianFilter::GaussianFilter(const GaussianFilter &iBrother)
{
_sigma = iBrother._sigma;
_maskSize = iBrother._maskSize;
_bound = iBrother._bound;
_storedMaskSize = iBrother._storedMaskSize;
_mask = new float[_maskSize * _maskSize];
memcpy(_mask, iBrother._mask, _maskSize * _maskSize * sizeof(float));
}
GaussianFilter &GaussianFilter::operator=(const GaussianFilter &iBrother)
{
_sigma = iBrother._sigma;
_maskSize = iBrother._maskSize;
_bound = iBrother._bound;
_storedMaskSize = iBrother._storedMaskSize;
_mask = new float[_storedMaskSize * _storedMaskSize];
memcpy(_mask, iBrother._mask, _storedMaskSize * _storedMaskSize * sizeof(float));
return *this;
}
GaussianFilter::~GaussianFilter()
{
delete[] _mask;
}
int GaussianFilter::computeMaskSize(float sigma)
{
int maskSize = int(floor(4 * sigma)) + 1;
if (0 == (maskSize % 2)) {
++maskSize;
}
return maskSize;
}
void GaussianFilter::setSigma(float sigma)
{
_sigma = sigma;
computeMask();
}
void GaussianFilter::computeMask()
{
delete[] _mask;
_maskSize = computeMaskSize(_sigma);
_storedMaskSize = (_maskSize + 1) >> 1;
_bound = _storedMaskSize - 1;
float norm = _sigma * _sigma * 2.0f * M_PI;
float invNorm = 1.0f / norm;
_mask = new float[_storedMaskSize * _storedMaskSize * sizeof(float)];
for (int i = 0; i < _storedMaskSize; ++i) {
for (int j = 0; j < _storedMaskSize; ++j) {
#if 0
_mask[i * _storedMaskSize + j] = exp(-(i * i + j * j) / (2.0 * _sigma * _sigma));
#else
_mask[i * _storedMaskSize + j] = invNorm * exp(-(i * i + j * j) / (2.0 * _sigma * _sigma));
#endif
}
}
}
} /* namespace Freestyle */

View File

@@ -0,0 +1,132 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Class to perform gaussian filtering operations on an image
*/
#include <cstdlib> // for abs
#include <string.h> // for memcpy
#include "../system/FreestyleConfig.h"
#include "MEM_guardedalloc.h"
namespace Freestyle {
class GaussianFilter {
protected:
/* The mask is a symmetrical 2d array (with respect to the middle point).
* Thus: `M(i,j) = M(-i,j) = M(i,-j) = M(-i,-j)`.
* For this reason, to represent a NxN array (N odd),
* we only store a `((N+1)/2)x((N+1)/2)` array. */
/** The sigma value of the gaussian function. */
float _sigma;
float *_mask;
int _bound;
/* The real mask size (must be odd), the size of the mask we store is:
* `((_maskSize+1)/2)*((_maskSize+1)/2))`. */
int _maskSize;
int _storedMaskSize; // (_maskSize+1)/2)
public:
GaussianFilter(float iSigma = 1.0f);
GaussianFilter(const GaussianFilter &);
GaussianFilter &operator=(const GaussianFilter &);
virtual ~GaussianFilter();
/** Returns the value for pixel x,y of image "map" after a gaussian blur, made using the sigma
* value. The sigma value determines the mask size (~ 2 x sigma).
* \param map: The image we wish to work on.
* The Map template must implement the following methods:
* - `float pixel(uint x, uint y) const;`
* - `uint width() const;`
* - `uint height() const;`
* \param x: The abscissa of the pixel where we want to evaluate the gaussian blur.
* \param y: The ordinate of the pixel where we want to evaluate the gaussian blur.
*/
template<class Map> float getSmoothedPixel(Map *map, int x, int y);
/** Compute the mask size and returns the REAL mask size ((2*_maskSize)-1)
* This method is provided for convenience.
*/
static int computeMaskSize(float sigma);
/** accessors */
inline float sigma() const
{
return _sigma;
}
inline int maskSize() const
{
return _maskSize;
}
inline int getBound()
{
return _bound;
}
/** modifiers */
void setSigma(float sigma);
#if 0
void SetMaskSize(int size)
{
_maskSize = size;
_storedMaskSize = (_maskSize + 1) >> 1;
}
#endif
protected:
void computeMask();
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:GaussianFilter")
};
/*
* #############################################
* #############################################
* #############################################
* ###### ######
* ###### I M P L E M E N T A T I O N ######
* ###### ######
* #############################################
* #############################################
* #############################################
*/
template<class Map> float GaussianFilter::getSmoothedPixel(Map *map, int x, int y)
{
// float sum = 0.0f;
float L = 0.0f;
int w = int(map->width()); // soc
int h = int(map->height()); // soc
// Current pixel is x,y
// Sum surrounding pixels L value:
for (int i = -_bound; i <= _bound; ++i) {
if ((y + i < 0) || (y + i >= h)) {
continue;
}
for (int j = -_bound; j <= _bound; ++j) {
if ((x + j < 0) || (x + j >= w)) {
continue;
}
float tmpL = map->pixel(x + j, y + i);
float m = _mask[abs(i) * _storedMaskSize + abs(j)];
L += m * tmpL;
// sum += m;
}
}
// L /= sum;
return L;
}
} /* namespace Freestyle */

View File

@@ -0,0 +1,401 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Class to encapsulate an array of RGB or Gray level values
*/
#include <string.h> // for memcpy
#include "MEM_guardedalloc.h"
namespace Freestyle {
//
// Image base class, for all types of images
//
///////////////////////////////////////////////////////////////////////////////
/** This class allows the storing of part of an image, while allowing a normal access to its pixel
* values. You can for example only a rectangle of sw*sh, whose lower-left corner is at (ox, oy),
* of an image of size w*h, and access these pixels using x,y coordinates specified in the whole
* image coordinate system.
*/
class FrsImage {
public:
/** Default constructor */
FrsImage()
{
_storedWidth = 0;
_storedHeight = 0;
_width = 0;
_height = 0;
_Ox = 0;
_Oy = 0;
}
/** Copy constructor */
FrsImage(const FrsImage &brother)
{
_storedWidth = brother._storedWidth;
_storedHeight = brother._storedHeight;
_width = brother._width;
_height = brother._height;
_Ox = brother._Ox;
_Oy = brother._Oy;
}
/** Builds an FrsImage from its width and height.
* The memory is allocated consequently.
*/
FrsImage(uint w, uint h)
{
_width = w;
_height = h;
_storedWidth = w;
_storedHeight = h;
_Ox = 0;
_Oy = 0;
}
/** Builds a partial-storing image.
* \param w:
* The width of the complete image
* \param h:
* The height of the complete image
* \param sw:
* The width of the rectangle that will actually be stored.
* \param sh:
* The height of the rectangle that will actually be stored.
* \param ox:
* The x-abscissa of the origin of the rectangle that will actually be stored.
* \param oy:
* The x-abscissa of the origin of the rectangle that will actually be stored.
*/
FrsImage(uint w, uint h, uint sw, uint sh, uint ox, uint oy)
{
_width = w;
_height = h;
_storedWidth = sw;
_storedHeight = sh;
_Ox = ox;
_Oy = oy;
}
/** Operator= */
FrsImage &operator=(const FrsImage &brother)
{
_width = brother._width;
_height = brother._height;
_storedWidth = brother._storedWidth;
_storedHeight = brother._storedHeight;
_Ox = brother._Ox;
_Oy = brother._Oy;
return *this;
}
/** Destructor */
virtual ~FrsImage() {}
/** Returns the width of the complete image */
inline uint width() const
{
return _width;
}
/** Returns the height of the complete image */
inline uint height() const
{
return _height;
}
/** Returns the gray value for pixel x,y */
virtual float pixel(uint x, uint y) const = 0;
/** Sets the array.
* \param array:
* The array containing the values we wish to store.
* Its size is sw*sh.
* \param width:
* The width of the complete image
* \param height:
* The height of the complete image
* \param sw:
* The width of the rectangle that will actually be stored.
* \param sh:
* The height of the rectangle that will actually be stored.
* \param x:
* The x-abscissa of the origin of the rectangle that will actually be stored.
* \param y:
* The x-abscissa of the origin of the rectangle that will actually be stored.
* \param copy:
* If true, the array is copied, otherwise the pointer is copied
*/
virtual void setArray(float *array,
uint width,
uint height,
uint sw,
uint sh,
uint x,
uint y,
bool copy = true) = 0;
/** Returns the array containing the pixels values.
* Its size is sw*sh, i.e. potentially a smaller rectangular part of the complete image.
*/
virtual float *getArray() = 0;
protected:
uint _width;
uint _height;
uint _storedWidth;
uint _storedHeight;
uint _Ox; // origin of the stored part
uint _Oy; // origin of the stored part
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:FrsImage")
};
//
// RGBImage
//
///////////////////////////////////////////////////////////////////////////////
class RGBImage : public FrsImage {
public:
RGBImage() : FrsImage()
{
_rgb = 0;
}
RGBImage(const RGBImage &brother) : FrsImage(brother)
{
_rgb = new float[3 * _storedWidth * _storedHeight];
memcpy(_rgb, brother._rgb, 3 * _storedWidth * _storedHeight * sizeof(float));
}
RGBImage(uint w, uint h) : FrsImage(w, h)
{
_rgb = new float[3 * _width * _height];
}
RGBImage(float *rgb, uint w, uint h) : FrsImage(w, h)
{
_rgb = new float[3 * _width * _height];
memcpy(_rgb, rgb, 3 * _width * _height * sizeof(float));
}
/** Builds an RGB partial image from the useful part buffer.
* \param rgb:
* The array of size 3*sw*sh containing the RGB values of the sw*sh pixels we need to stored.
* These sw*sh pixels constitute a rectangular part of a bigger
* RGB image containing w*h pixels.
* \param w:
* The width of the complete image
* \param h:
* The height of the complete image
* \param sw:
* The width of the part of the image we want to store and work on
* \param sh:
* The height of the part of the image we want to store and work on
*/
RGBImage(float *rgb, uint w, uint h, uint sw, uint sh, uint ox, uint oy)
: FrsImage(w, h, sw, sh, ox, oy)
{
_rgb = new float[3 * _storedWidth * _storedHeight];
memcpy(_rgb, rgb, 3 * _storedWidth * _storedHeight * sizeof(float));
}
RGBImage &operator=(const RGBImage &brother)
{
dynamic_cast<FrsImage &>(*this) = brother;
_rgb = new float[3 * _storedWidth * _storedHeight];
memcpy(_rgb, brother._rgb, 3 * _storedWidth * _storedHeight * sizeof(float));
return *this;
}
virtual ~RGBImage()
{
if (_rgb) {
delete[] _rgb;
}
}
inline float getR(uint x, uint y) const
{
return _rgb[3 * (y - _Oy) * _storedWidth + (x - _Ox) * 3];
}
inline float getG(uint x, uint y) const
{
return _rgb[3 * (y - _Oy) * _storedWidth + (x - _Ox) * 3 + 1];
}
inline float getB(uint x, uint y) const
{
return _rgb[3 * (y - _Oy) * _storedWidth + (x - _Ox) * 3 + 2];
}
virtual void setPixel(uint x, uint y, float r, float g, float b)
{
float *tmp = &(_rgb[3 * (y - _Oy) * _storedWidth + (x - _Ox) * 3]);
*tmp = r;
tmp++;
*tmp = g;
tmp++;
*tmp = b;
}
virtual float pixel(uint x, uint y) const
{
float res = 0.0f;
float *tmp = &(_rgb[3 * (y - _Oy) * _storedWidth + (x - _Ox) * 3]);
res += 11.0f * (*tmp);
tmp++;
res += 16.0f * (*tmp);
tmp++;
res += 5.0f * (*tmp);
return res / 32.0f;
}
/** Sets the RGB array.
* copy
* If true, the array is copied, otherwise the pointer is copied
*/
virtual void setArray(
float *rgb, uint width, uint height, uint sw, uint sh, uint x, uint y, bool copy = true)
{
_width = width;
_height = height;
_storedWidth = sw;
_storedHeight = sh;
_Ox = x;
_Oy = y;
if (!copy) {
_rgb = rgb;
return;
}
memcpy(_rgb, rgb, 3 * _storedWidth * _storedHeight * sizeof(float));
}
virtual float *getArray()
{
return _rgb;
}
protected:
float *_rgb;
};
//
// GrayImage
//
///////////////////////////////////////////////////////////////////////////////
class GrayImage : public FrsImage {
public:
GrayImage() : FrsImage()
{
_lvl = 0;
}
GrayImage(const GrayImage &brother) : FrsImage(brother)
{
_lvl = new float[_storedWidth * _storedHeight];
memcpy(_lvl, brother._lvl, _storedWidth * _storedHeight * sizeof(*_lvl));
}
/** Builds an empty gray image */
GrayImage(uint w, uint h) : FrsImage(w, h)
{
_lvl = new float[_width * _height];
}
GrayImage(float *lvl, uint w, uint h) : FrsImage(w, h)
{
_lvl = new float[_width * _height];
memcpy(_lvl, lvl, _width * _height * sizeof(*_lvl));
}
/** Builds a partial image from the useful part buffer.
* \param lvl:
* The array of size sw*sh containing the gray values of the sw*sh pixels we need to stored.
* These sw*sh pixels constitute a rectangular part of a bigger
* gray image containing w*h pixels.
* \param w:
* The width of the complete image
* \param h:
* The height of the complete image
* \param sw:
* The width of the part of the image we want to store and work on
* \param sh:
* The height of the part of the image we want to store and work on
*/
GrayImage(float *lvl, uint w, uint h, uint sw, uint sh, uint ox, uint oy)
: FrsImage(w, h, sw, sh, ox, oy)
{
_lvl = new float[_storedWidth * _storedHeight];
memcpy(_lvl, lvl, _storedWidth * _storedHeight * sizeof(float));
}
GrayImage &operator=(const GrayImage &brother)
{
dynamic_cast<FrsImage &>(*this) = brother;
_lvl = new float[_storedWidth * _storedHeight];
memcpy(_lvl, brother._lvl, _storedWidth * _storedHeight * sizeof(float));
return *this;
}
virtual ~GrayImage()
{
if (_lvl) {
delete[] _lvl;
}
}
inline void setPixel(uint x, uint y, float v)
{
_lvl[(y - _Oy) * _storedWidth + (x - _Ox)] = v;
}
inline float pixel(uint x, uint y) const
{
return _lvl[(y - _Oy) * _storedWidth + (x - _Ox)];
}
/** Sets the array.
* copy
* If true, the array is copied, otherwise the pounsigneder is copied
*/
void setArray(
float *lvl, uint width, uint height, uint sw, uint sh, uint x, uint y, bool copy = true)
{
_width = width;
_height = height;
_storedWidth = sw;
_storedHeight = sh;
_Ox = x;
_Oy = y;
if (!copy) {
_lvl = lvl;
return;
}
memcpy(_lvl, lvl, _storedWidth * _storedHeight * sizeof(float));
}
/** Returns the array containing the gray values. */
virtual float *getArray()
{
return _lvl;
}
protected:
float *_lvl;
};
} /* namespace Freestyle */

View File

@@ -0,0 +1,181 @@
/* SPDX-FileCopyrightText: 2008-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
* \brief Class to represent a pyramid of images
*/
#include <iostream>
#include "GaussianFilter.h"
#include "Image.h"
#include "ImagePyramid.h"
#include "BLI_sys_types.h"
using namespace std;
namespace Freestyle {
#if 0
ImagePyramid::ImagePyramid(const GrayImage &level0, uint nbLevels)
{
BuildPyramid(level0, nbLevels);
}
#endif
ImagePyramid::ImagePyramid(const ImagePyramid & /*iBrother*/)
{
if (!_levels.empty()) {
for (vector<GrayImage *>::iterator im = _levels.begin(), imend = _levels.end(); im != imend;
++im)
{
_levels.push_back(new GrayImage(**im));
}
}
}
ImagePyramid::~ImagePyramid()
{
if (!_levels.empty()) {
for (vector<GrayImage *>::iterator im = _levels.begin(), imend = _levels.end(); im != imend;
++im)
{
delete (*im);
}
_levels.clear();
}
}
GrayImage *ImagePyramid::getLevel(int l)
{
return _levels[l];
}
float ImagePyramid::pixel(int x, int y, int level)
{
GrayImage *img = _levels[level];
if (0 == level) {
return img->pixel(x, y);
}
uint i = 1 << level;
uint sx = x >> level;
uint sy = y >> level;
if (sx >= img->width()) {
sx = img->width() - 1;
}
if (sy >= img->height()) {
sy = img->height() - 1;
}
// bilinear interpolation
float A = i * (sx + 1) - x;
float B = x - i * sx;
float C = i * (sy + 1) - y;
float D = y - i * sy;
float P1(0), P2(0);
P1 = A * img->pixel(sx, sy);
if (sx < img->width() - 1) {
if (x % i != 0) {
P1 += B * img->pixel(sx + 1, sy);
}
}
else {
P1 += B * img->pixel(sx, sy);
}
if (sy < img->height() - 1) {
if (y % i != 0) {
P2 = A * img->pixel(sx, sy + 1);
if (sx < img->width() - 1) {
if (x % i != 0) {
P2 += B * img->pixel(sx + 1, sy + 1);
}
}
else {
P2 += B * img->pixel(sx, sy + 1);
}
}
}
else {
P2 = P1;
}
return (1.0f / float(1 << (2 * level))) * (C * P1 + D * P2);
}
int ImagePyramid::width(int level)
{
return _levels[level]->width();
}
int ImagePyramid::height(int level)
{
return _levels[level]->height();
}
GaussianPyramid::GaussianPyramid(const GrayImage &level0, uint nbLevels, float iSigma)
{
_sigma = iSigma;
BuildPyramid(level0, nbLevels);
}
GaussianPyramid::GaussianPyramid(GrayImage *level0, uint nbLevels, float iSigma)
{
_sigma = iSigma;
BuildPyramid(level0, nbLevels);
}
GaussianPyramid::GaussianPyramid(const GaussianPyramid &iBrother) : ImagePyramid(iBrother)
{
_sigma = iBrother._sigma;
}
void GaussianPyramid::BuildPyramid(const GrayImage &level0, uint nbLevels)
{
GrayImage *pLevel = new GrayImage(level0);
BuildPyramid(pLevel, nbLevels);
}
void GaussianPyramid::BuildPyramid(GrayImage *level0, uint nbLevels)
{
GrayImage *pLevel = level0;
_levels.push_back(pLevel);
GaussianFilter gf(_sigma);
// build the nbLevels:
uint w = pLevel->width();
uint h = pLevel->height();
if (nbLevels != 0) {
for (uint i = 0; i < nbLevels; ++i) { // soc
w = pLevel->width() >> 1;
h = pLevel->height() >> 1;
GrayImage *img = new GrayImage(w, h);
for (uint y = 0; y < h; ++y) {
for (uint x = 0; x < w; ++x) {
float v = gf.getSmoothedPixel<GrayImage>(pLevel, 2 * x, 2 * y);
img->setPixel(x, y, v);
}
}
_levels.push_back(img);
pLevel = img;
}
}
else {
while ((w > 1) && (h > 1)) {
w = pLevel->width() >> 1;
h = pLevel->height() >> 1;
GrayImage *img = new GrayImage(w, h);
for (uint y = 0; y < h; ++y) {
for (uint x = 0; x < w; ++x) {
float v = gf.getSmoothedPixel<GrayImage>(pLevel, 2 * x, 2 * y);
img->setPixel(x, y, v);
}
}
_levels.push_back(img);
pLevel = img;
}
}
}
} /* namespace Freestyle */

View File

@@ -0,0 +1,94 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Class to represent a pyramid of images
*/
#include <vector>
#include "../system/FreestyleConfig.h"
#include "MEM_guardedalloc.h"
namespace Freestyle {
class GrayImage;
class ImagePyramid {
protected:
std::vector<GrayImage *> _levels;
public:
ImagePyramid() {}
ImagePyramid(const ImagePyramid &iBrother);
// ImagePyramid(const GrayImage& level0, uint nbLevels);
virtual ~ImagePyramid();
/** Builds the pyramid.
* must be overloaded by inherited classes.
* if nbLevels==0, the complete pyramid is built
*/
virtual void BuildPyramid(const GrayImage &level0, uint nbLevels) = 0;
/** Builds a pyramid without copying the base level */
virtual void BuildPyramid(GrayImage *level0, uint nbLevels) = 0;
virtual GrayImage *getLevel(int l);
/** Returns the pixel x,y using bilinear interpolation.
* \param x:
* the abscissa specified in the finest level coordinate system
* \param y:
* the ordinate specified in the finest level coordinate system
* \param level:
* the level from which we want the pixel to be evaluated
*/
virtual float pixel(int x, int y, int level = 0);
/** Returns the width of the level-th level image */
virtual int width(int level = 0);
/** Returns the height of the level-th level image */
virtual int height(int level = 0);
/** Returns the number of levels in the pyramid */
inline int getNumberOfLevels() const
{
return _levels.size();
}
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:ImagePyramid")
};
class GaussianPyramid : public ImagePyramid {
protected:
float _sigma;
public:
GaussianPyramid(float iSigma = 1.0f) : ImagePyramid()
{
_sigma = iSigma;
}
GaussianPyramid(const GrayImage &level0, uint nbLevels, float iSigma = 1.0f);
GaussianPyramid(GrayImage *level0, uint nbLevels, float iSigma = 1.0f);
GaussianPyramid(const GaussianPyramid &iBrother);
virtual ~GaussianPyramid() {}
virtual void BuildPyramid(const GrayImage &level0, uint nbLevels);
virtual void BuildPyramid(GrayImage *level0, uint nbLevels);
/* accessors */
inline float getSigma() const
{
return _sigma;
}
/* modifiers */
};
} /* namespace Freestyle */

View File

@@ -0,0 +1,106 @@
/* SPDX-FileCopyrightText: 2004-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_BBox.h"
using namespace Freestyle;
using namespace Freestyle::Geometry;
///////////////////////////////////////////////////////////////////////////////////////////
//-------------------MODULE INITIALIZATION--------------------------------
int BBox_Init(PyObject *module)
{
if (module == nullptr) {
return -1;
}
if (PyType_Ready(&BBox_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "BBox", (PyObject *)&BBox_Type);
return 0;
}
//------------------------INSTANCE METHODS ----------------------------------
PyDoc_STRVAR(
/* Wrap. */
BBox_doc,
"Class for representing a bounding box.\n"
"\n"
".. method:: __init__()\n"
"\n"
" Default constructor.\n");
static int BBox_init(BPy_BBox *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {nullptr};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "", (char **)kwlist)) {
return -1;
}
self->bb = new BBox<Vec3r>();
return 0;
}
static void BBox_dealloc(BPy_BBox *self)
{
delete self->bb;
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *BBox_repr(BPy_BBox *self)
{
return PyUnicode_FromFormat("BBox - address: %p", self->bb);
}
/*-----------------------BPy_BBox type definition ------------------------------*/
PyTypeObject BBox_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "BBox",
/*tp_basicsize*/ sizeof(BPy_BBox),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ (destructor)BBox_dealloc,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ (reprfunc)BBox_repr,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ nullptr,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
/*tp_doc*/ BBox_doc,
/*tp_traverse*/ nullptr,
/*tp_clear*/ nullptr,
/*tp_richcompare*/ nullptr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ nullptr,
/*tp_members*/ nullptr,
/*tp_getset*/ nullptr,
/*tp_base*/ nullptr,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ (initproc)BBox_init,
/*tp_alloc*/ nullptr,
/*tp_new*/ PyType_GenericNew,
};
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,34 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
extern "C" {
#include <Python.h>
}
#include "../geometry/BBox.h"
#include "../geometry/Geom.h"
///////////////////////////////////////////////////////////////////////////////////////////
extern PyTypeObject BBox_Type;
#define BPy_BBox_Check(v) (PyObject_IsInstance((PyObject *)v, (PyObject *)&BBox_Type))
/*---------------------------Python BPy_BBox structure definition----------*/
struct BPy_BBox {
PyObject_HEAD
Freestyle::BBox<Freestyle::Geometry::Vec3r> *bb;
};
/*---------------------------Python BPy_BBox visible prototypes-----------*/
int BBox_Init(PyObject *module);
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,174 @@
/* SPDX-FileCopyrightText: 2004-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_BinaryPredicate0D.h"
#include "BPy_Convert.h"
#include "BPy_Interface0D.h"
using namespace Freestyle;
///////////////////////////////////////////////////////////////////////////////////////////
//-------------------MODULE INITIALIZATION--------------------------------
int BinaryPredicate0D_Init(PyObject *module)
{
if (module == nullptr) {
return -1;
}
if (PyType_Ready(&BinaryPredicate0D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "BinaryPredicate0D", (PyObject *)&BinaryPredicate0D_Type);
return 0;
}
//------------------------INSTANCE METHODS ----------------------------------
PyDoc_STRVAR(
/* Wrap. */
BinaryPredicate0D___doc__,
"Base class for binary predicates working on :class:`Interface0D`\n"
"objects. A BinaryPredicate0D is typically an ordering relation\n"
"between two Interface0D objects. The predicate evaluates a relation\n"
"between the two Interface0D instances and returns a boolean value (true\n"
"or false). It is used by invoking the __call__() method.\n"
"\n"
".. method:: __init__()\n"
"\n"
" Default constructor.\n"
"\n"
".. method:: __call__(inter1, inter2)\n"
"\n"
" Must be overload by inherited classes. It evaluates a relation\n"
" between two Interface0D objects.\n"
"\n"
" :param inter1: The first Interface0D object.\n"
" :type inter1: :class:`Interface0D`\n"
" :param inter2: The second Interface0D object.\n"
" :type inter2: :class:`Interface0D`\n"
" :return: True or false.\n"
" :rtype: bool\n");
static int BinaryPredicate0D___init__(BPy_BinaryPredicate0D *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {nullptr};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "", (char **)kwlist)) {
return -1;
}
self->bp0D = new BinaryPredicate0D();
self->bp0D->py_bp0D = (PyObject *)self;
return 0;
}
static void BinaryPredicate0D___dealloc__(BPy_BinaryPredicate0D *self)
{
delete self->bp0D;
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *BinaryPredicate0D___repr__(BPy_BinaryPredicate0D *self)
{
return PyUnicode_FromFormat("type: %s - address: %p", Py_TYPE(self)->tp_name, self->bp0D);
}
static PyObject *BinaryPredicate0D___call__(BPy_BinaryPredicate0D *self,
PyObject *args,
PyObject *kwds)
{
static const char *kwlist[] = {"inter1", "inter2", nullptr};
BPy_Interface0D *obj1, *obj2;
if (!PyArg_ParseTupleAndKeywords(
args, kwds, "O!O!", (char **)kwlist, &Interface0D_Type, &obj1, &Interface0D_Type, &obj2))
{
return nullptr;
}
if (typeid(*(self->bp0D)) == typeid(BinaryPredicate0D)) {
PyErr_SetString(PyExc_TypeError, "__call__ method not properly overridden");
return nullptr;
}
if (self->bp0D->operator()(*(obj1->if0D), *(obj2->if0D)) < 0) {
if (!PyErr_Occurred()) {
string class_name(Py_TYPE(self)->tp_name);
PyErr_SetString(PyExc_RuntimeError, (class_name + " __call__ method failed").c_str());
}
return nullptr;
}
return PyBool_from_bool(self->bp0D->result);
}
/*----------------------BinaryPredicate0D get/setters ----------------------------*/
PyDoc_STRVAR(
/* Wrap. */
BinaryPredicate0D_name_doc,
"The name of the binary 0D predicate.\n"
"\n"
":type: str\n");
static PyObject *BinaryPredicate0D_name_get(BPy_BinaryPredicate0D *self, void * /*closure*/)
{
return PyUnicode_FromString(Py_TYPE(self)->tp_name);
}
static PyGetSetDef BPy_BinaryPredicate0D_getseters[] = {
{"name",
(getter)BinaryPredicate0D_name_get,
(setter) nullptr,
BinaryPredicate0D_name_doc,
nullptr},
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
};
/*-----------------------BPy_BinaryPredicate0D type definition ------------------------------*/
PyTypeObject BinaryPredicate0D_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "BinaryPredicate0D",
/*tp_basicsize*/ sizeof(BPy_BinaryPredicate0D),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ (destructor)BinaryPredicate0D___dealloc__,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ (reprfunc)BinaryPredicate0D___repr__,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ (ternaryfunc)BinaryPredicate0D___call__,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
/*tp_doc*/ BinaryPredicate0D___doc__,
/*tp_traverse*/ nullptr,
/*tp_clear*/ nullptr,
/*tp_richcompare*/ nullptr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ nullptr,
/*tp_members*/ nullptr,
/*tp_getset*/ BPy_BinaryPredicate0D_getseters,
/*tp_base*/ nullptr,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ (initproc)BinaryPredicate0D___init__,
/*tp_alloc*/ nullptr,
/*tp_new*/ PyType_GenericNew,
};
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,34 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
extern "C" {
#include <Python.h>
}
#include "../stroke/Predicates0D.h"
///////////////////////////////////////////////////////////////////////////////////////////
extern PyTypeObject BinaryPredicate0D_Type;
#define BPy_BinaryPredicate0D_Check(v) \
(PyObject_IsInstance((PyObject *)v, (PyObject *)&BinaryPredicate0D_Type))
/*---------------------------Python BPy_BinaryPredicate0D structure definition----------*/
struct BPy_BinaryPredicate0D {
PyObject_HEAD
Freestyle::BinaryPredicate0D *bp0D;
};
/*---------------------------Python BPy_BinaryPredicate0D visible prototypes-----------*/
int BinaryPredicate0D_Init(PyObject *module);
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,205 @@
/* SPDX-FileCopyrightText: 2004-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_BinaryPredicate1D.h"
#include "BPy_Convert.h"
#include "BPy_Interface1D.h"
#include "BinaryPredicate1D/BPy_FalseBP1D.h"
#include "BinaryPredicate1D/BPy_Length2DBP1D.h"
#include "BinaryPredicate1D/BPy_SameShapeIdBP1D.h"
#include "BinaryPredicate1D/BPy_TrueBP1D.h"
#include "BinaryPredicate1D/BPy_ViewMapGradientNormBP1D.h"
using namespace Freestyle;
///////////////////////////////////////////////////////////////////////////////////////////
//-------------------MODULE INITIALIZATION--------------------------------
int BinaryPredicate1D_Init(PyObject *module)
{
if (module == nullptr) {
return -1;
}
if (PyType_Ready(&BinaryPredicate1D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "BinaryPredicate1D", (PyObject *)&BinaryPredicate1D_Type);
if (PyType_Ready(&FalseBP1D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "FalseBP1D", (PyObject *)&FalseBP1D_Type);
if (PyType_Ready(&Length2DBP1D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "Length2DBP1D", (PyObject *)&Length2DBP1D_Type);
if (PyType_Ready(&SameShapeIdBP1D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "SameShapeIdBP1D", (PyObject *)&SameShapeIdBP1D_Type);
if (PyType_Ready(&TrueBP1D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "TrueBP1D", (PyObject *)&TrueBP1D_Type);
if (PyType_Ready(&ViewMapGradientNormBP1D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(
module, "ViewMapGradientNormBP1D", (PyObject *)&ViewMapGradientNormBP1D_Type);
return 0;
}
//------------------------INSTANCE METHODS ----------------------------------
PyDoc_STRVAR(
/* Wrap. */
BinaryPredicate1D___doc__,
"Base class for binary predicates working on :class:`Interface1D`\n"
"objects. A BinaryPredicate1D is typically an ordering relation\n"
"between two Interface1D objects. The predicate evaluates a relation\n"
"between the two Interface1D instances and returns a boolean value (true\n"
"or false). It is used by invoking the __call__() method.\n"
"\n"
".. method:: __init__()\n"
"\n"
" Default constructor.\n"
"\n"
".. method:: __call__(inter1, inter2)\n"
"\n"
" Must be overload by inherited classes. It evaluates a relation\n"
" between two Interface1D objects.\n"
"\n"
" :param inter1: The first Interface1D object.\n"
" :type inter1: :class:`Interface1D`\n"
" :param inter2: The second Interface1D object.\n"
" :type inter2: :class:`Interface1D`\n"
" :return: True or false.\n"
" :rtype: bool\n");
static int BinaryPredicate1D___init__(BPy_BinaryPredicate1D *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {nullptr};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "", (char **)kwlist)) {
return -1;
}
self->bp1D = new BinaryPredicate1D();
self->bp1D->py_bp1D = (PyObject *)self;
return 0;
}
static void BinaryPredicate1D___dealloc__(BPy_BinaryPredicate1D *self)
{
delete self->bp1D;
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *BinaryPredicate1D___repr__(BPy_BinaryPredicate1D *self)
{
return PyUnicode_FromFormat("type: %s - address: %p", Py_TYPE(self)->tp_name, self->bp1D);
}
static PyObject *BinaryPredicate1D___call__(BPy_BinaryPredicate1D *self,
PyObject *args,
PyObject *kwds)
{
static const char *kwlist[] = {"inter1", "inter2", nullptr};
BPy_Interface1D *obj1, *obj2;
if (!PyArg_ParseTupleAndKeywords(
args, kwds, "O!O!", (char **)kwlist, &Interface1D_Type, &obj1, &Interface1D_Type, &obj2))
{
return nullptr;
}
if (typeid(*(self->bp1D)) == typeid(BinaryPredicate1D)) {
PyErr_SetString(PyExc_TypeError, "__call__ method not properly overridden");
return nullptr;
}
if (self->bp1D->operator()(*(obj1->if1D), *(obj2->if1D)) < 0) {
if (!PyErr_Occurred()) {
string class_name(Py_TYPE(self)->tp_name);
PyErr_SetString(PyExc_RuntimeError, (class_name + " __call__ method failed").c_str());
}
return nullptr;
}
return PyBool_from_bool(self->bp1D->result);
}
/*----------------------BinaryPredicate0D get/setters ----------------------------*/
PyDoc_STRVAR(
/* Wrap. */
BinaryPredicate1D_name_doc,
"The name of the binary 1D predicate.\n"
"\n"
":type: str\n");
static PyObject *BinaryPredicate1D_name_get(BPy_BinaryPredicate1D *self, void * /*closure*/)
{
return PyUnicode_FromString(Py_TYPE(self)->tp_name);
}
static PyGetSetDef BPy_BinaryPredicate1D_getseters[] = {
{"name",
(getter)BinaryPredicate1D_name_get,
(setter) nullptr,
BinaryPredicate1D_name_doc,
nullptr},
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
};
/*-----------------------BPy_BinaryPredicate1D type definition ------------------------------*/
PyTypeObject BinaryPredicate1D_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "BinaryPredicate1D",
/*tp_basicsize*/ sizeof(BPy_BinaryPredicate1D),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ (destructor)BinaryPredicate1D___dealloc__,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ (reprfunc)BinaryPredicate1D___repr__,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ (ternaryfunc)BinaryPredicate1D___call__,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
/*tp_doc*/ BinaryPredicate1D___doc__,
/*tp_traverse*/ nullptr,
/*tp_clear*/ nullptr,
/*tp_richcompare*/ nullptr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ nullptr,
/*tp_members*/ nullptr,
/*tp_getset*/ BPy_BinaryPredicate1D_getseters,
/*tp_base*/ nullptr,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ (initproc)BinaryPredicate1D___init__,
/*tp_alloc*/ nullptr,
/*tp_new*/ PyType_GenericNew,
};
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,34 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
extern "C" {
#include <Python.h>
}
#include "../stroke/Predicates1D.h"
///////////////////////////////////////////////////////////////////////////////////////////
extern PyTypeObject BinaryPredicate1D_Type;
#define BPy_BinaryPredicate1D_Check(v) \
(PyObject_IsInstance((PyObject *)v, (PyObject *)&BinaryPredicate1D_Type))
/*---------------------------Python BPy_BinaryPredicate1D structure definition----------*/
struct BPy_BinaryPredicate1D {
PyObject_HEAD
Freestyle::BinaryPredicate1D *bp1D;
};
/*---------------------------Python BPy_BinaryPredicate1D visible prototypes-----------*/
int BinaryPredicate1D_Init(PyObject *module);
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,342 @@
/* SPDX-FileCopyrightText: 2009-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_ContextFunctions.h"
#include "BPy_Convert.h"
#include "../stroke/ContextFunctions.h"
#include "BLI_sys_types.h"
using namespace Freestyle;
///////////////////////////////////////////////////////////////////////////////////////////
//------------------------ MODULE FUNCTIONS ----------------------------------
PyDoc_STRVAR(
/* Wrap. */
ContextFunctions_get_time_stamp___doc__,
".. function:: get_time_stamp()\n"
"\n"
" Returns the system time stamp.\n"
"\n"
" :return: The system time stamp.\n"
" :rtype: int\n");
static PyObject *ContextFunctions_get_time_stamp(PyObject * /*self*/)
{
return PyLong_FromLong(ContextFunctions::GetTimeStampCF());
}
PyDoc_STRVAR(
/* Wrap. */
ContextFunctions_get_canvas_width___doc__,
".. function:: get_canvas_width()\n"
"\n"
" Returns the canvas width.\n"
"\n"
" :return: The canvas width.\n"
" :rtype: int\n");
static PyObject *ContextFunctions_get_canvas_width(PyObject * /*self*/)
{
return PyLong_FromLong(ContextFunctions::GetCanvasWidthCF());
}
PyDoc_STRVAR(
/* Wrap. */
ContextFunctions_get_canvas_height___doc__,
".. function:: get_canvas_height()\n"
"\n"
" Returns the canvas height.\n"
"\n"
" :return: The canvas height.\n"
" :rtype: int\n");
static PyObject *ContextFunctions_get_canvas_height(PyObject * /*self*/)
{
return PyLong_FromLong(ContextFunctions::GetCanvasHeightCF());
}
PyDoc_STRVAR(
/* Wrap. */
ContextFunctions_get_border___doc__,
".. function:: get_border()\n"
"\n"
" Returns the border.\n"
"\n"
" :return: A tuple of 4 numbers (xmin, ymin, xmax, ymax).\n"
" :rtype: tuple[int, int, int, int]\n");
static PyObject *ContextFunctions_get_border(PyObject * /*self*/)
{
BBox<Vec2i> border(ContextFunctions::GetBorderCF());
PyObject *v = PyTuple_New(4);
PyTuple_SET_ITEMS(v,
PyLong_FromLong(border.getMin().x()),
PyLong_FromLong(border.getMin().y()),
PyLong_FromLong(border.getMax().x()),
PyLong_FromLong(border.getMax().y()));
return v;
}
PyDoc_STRVAR(
/* Wrap. */
ContextFunctions_load_map___doc__,
".. function:: load_map(file_name, map_name, num_levels=4, sigma=1.0)\n"
"\n"
" Loads an image map for further reading.\n"
"\n"
" :param file_name: The name of the image file.\n"
" :type file_name: str\n"
" :param map_name: The name that will be used to access this image.\n"
" :type map_name: str\n"
" :param num_levels: The number of levels in the map pyramid\n"
" (default = 4). If num_levels == 0, the complete pyramid is\n"
" built.\n"
" :type num_levels: int\n"
" :param sigma: The sigma value of the gaussian function.\n"
" :type sigma: float\n");
static PyObject *ContextFunctions_load_map(PyObject * /*self*/, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"file_name", "map_name", "num_levels", "sigma", nullptr};
char *fileName, *mapName;
uint nbLevels = 4;
float sigma = 1.0;
if (!PyArg_ParseTupleAndKeywords(
args, kwds, "ss|If", (char **)kwlist, &fileName, &mapName, &nbLevels, &sigma))
{
return nullptr;
}
ContextFunctions::LoadMapCF(fileName, mapName, nbLevels, sigma);
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
ContextFunctions_read_map_pixel___doc__,
".. function:: read_map_pixel(map_name, level, x, y)\n"
"\n"
" Reads a pixel in a user-defined map.\n"
"\n"
" :param map_name: The name of the map.\n"
" :type map_name: str\n"
" :param level: The level of the pyramid in which we wish to read the\n"
" pixel.\n"
" :type level: int\n"
" :param x: The x coordinate of the pixel we wish to read. The origin\n"
" is in the lower-left corner.\n"
" :type x: int\n"
" :param y: The y coordinate of the pixel we wish to read. The origin\n"
" is in the lower-left corner.\n"
" :type y: int\n"
" :return: The floating-point value stored for that pixel.\n"
" :rtype: float\n");
static PyObject *ContextFunctions_read_map_pixel(PyObject * /*self*/,
PyObject *args,
PyObject *kwds)
{
static const char *kwlist[] = {"map_name", "level", "x", "y", nullptr};
char *mapName;
int level;
uint x, y;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "siII", (char **)kwlist, &mapName, &level, &x, &y))
{
return nullptr;
}
return PyFloat_FromDouble(ContextFunctions::ReadMapPixelCF(mapName, level, x, y));
}
PyDoc_STRVAR(
/* Wrap. */
ContextFunctions_read_complete_view_map_pixel___doc__,
".. function:: read_complete_view_map_pixel(level, x, y)\n"
"\n"
" Reads a pixel in the complete view map.\n"
"\n"
" :param level: The level of the pyramid in which we wish to read the\n"
" pixel.\n"
" :type level: int\n"
" :param x: The x coordinate of the pixel we wish to read. The origin\n"
" is in the lower-left corner.\n"
" :type x: int\n"
" :param y: The y coordinate of the pixel we wish to read. The origin\n"
" is in the lower-left corner.\n"
" :type y: int\n"
" :return: The floating-point value stored for that pixel.\n"
" :rtype: float\n");
static PyObject *ContextFunctions_read_complete_view_map_pixel(PyObject * /*self*/,
PyObject *args,
PyObject *kwds)
{
static const char *kwlist[] = {"level", "x", "y", nullptr};
int level;
uint x, y;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "iII", (char **)kwlist, &level, &x, &y)) {
return nullptr;
}
return PyFloat_FromDouble(ContextFunctions::ReadCompleteViewMapPixelCF(level, x, y));
}
PyDoc_STRVAR(
/* Wrap. */
ContextFunctions_read_directional_view_map_pixel___doc__,
".. function:: read_directional_view_map_pixel(orientation, level, x, y)\n"
"\n"
" Reads a pixel in one of the oriented view map images.\n"
"\n"
" :param orientation: The number telling which orientation we want to\n"
" check.\n"
" :type orientation: int\n"
" :param level: The level of the pyramid in which we wish to read the\n"
" pixel.\n"
" :type level: int\n"
" :param x: The x coordinate of the pixel we wish to read. The origin\n"
" is in the lower-left corner.\n"
" :type x: int\n"
" :param y: The y coordinate of the pixel we wish to read. The origin\n"
" is in the lower-left corner.\n"
" :type y: int\n"
" :return: The floating-point value stored for that pixel.\n"
" :rtype: float\n");
static PyObject *ContextFunctions_read_directional_view_map_pixel(PyObject * /*self*/,
PyObject *args,
PyObject *kwds)
{
static const char *kwlist[] = {"orientation", "level", "x", "y", nullptr};
int orientation, level;
uint x, y;
if (!PyArg_ParseTupleAndKeywords(
args, kwds, "iiII", (char **)kwlist, &orientation, &level, &x, &y))
{
return nullptr;
}
return PyFloat_FromDouble(
ContextFunctions::ReadDirectionalViewMapPixelCF(orientation, level, x, y));
}
PyDoc_STRVAR(
/* Wrap. */
ContextFunctions_get_selected_fedge___doc__,
".. function:: get_selected_fedge()\n"
"\n"
" Returns the selected FEdge.\n"
"\n"
" :return: The selected FEdge.\n"
" :rtype: :class:`FEdge`\n");
static PyObject *ContextFunctions_get_selected_fedge(PyObject * /*self*/)
{
FEdge *fe = ContextFunctions::GetSelectedFEdgeCF();
if (fe) {
return Any_BPy_FEdge_from_FEdge(*fe);
}
Py_RETURN_NONE;
}
/*-----------------------ContextFunctions module docstring-------------------------------*/
PyDoc_STRVAR(
/* Wrap. */
module_docstring,
"The Blender Freestyle.ContextFunctions submodule\n"
"\n");
/*-----------------------ContextFunctions module functions definitions-------------------*/
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wcast-function-type"
# else
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
# endif
#endif
static PyMethodDef module_functions[] = {
{"get_time_stamp",
(PyCFunction)ContextFunctions_get_time_stamp,
METH_NOARGS,
ContextFunctions_get_time_stamp___doc__},
{"get_canvas_width",
(PyCFunction)ContextFunctions_get_canvas_width,
METH_NOARGS,
ContextFunctions_get_canvas_width___doc__},
{"get_canvas_height",
(PyCFunction)ContextFunctions_get_canvas_height,
METH_NOARGS,
ContextFunctions_get_canvas_height___doc__},
{"get_border",
(PyCFunction)ContextFunctions_get_border,
METH_NOARGS,
ContextFunctions_get_border___doc__},
{"load_map",
(PyCFunction)ContextFunctions_load_map,
METH_VARARGS | METH_KEYWORDS,
ContextFunctions_load_map___doc__},
{"read_map_pixel",
(PyCFunction)ContextFunctions_read_map_pixel,
METH_VARARGS | METH_KEYWORDS,
ContextFunctions_read_map_pixel___doc__},
{"read_complete_view_map_pixel",
(PyCFunction)ContextFunctions_read_complete_view_map_pixel,
METH_VARARGS | METH_KEYWORDS,
ContextFunctions_read_complete_view_map_pixel___doc__},
{"read_directional_view_map_pixel",
(PyCFunction)ContextFunctions_read_directional_view_map_pixel,
METH_VARARGS | METH_KEYWORDS,
ContextFunctions_read_directional_view_map_pixel___doc__},
{"get_selected_fedge",
(PyCFunction)ContextFunctions_get_selected_fedge,
METH_NOARGS,
ContextFunctions_get_selected_fedge___doc__},
{nullptr, nullptr, 0, nullptr},
};
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic pop
# else
# pragma GCC diagnostic pop
# endif
#endif
/*-----------------------ContextFunctions module definition--------------------------------*/
static PyModuleDef module_definition = {
/*m_base*/ PyModuleDef_HEAD_INIT,
/*m_name*/ "Freestyle.ContextFunctions",
/*m_doc*/ module_docstring,
/*m_size*/ -1,
/*m_methods*/ module_functions,
/*m_slots*/ nullptr,
/*m_traverse*/ nullptr,
/*m_clear*/ nullptr,
/*m_free*/ nullptr,
};
//------------------- MODULE INITIALIZATION --------------------------------
int ContextFunctions_Init(PyObject *module)
{
PyObject *m;
if (module == nullptr) {
return -1;
}
m = PyModule_Create(&module_definition);
if (m == nullptr) {
return -1;
}
PyModule_AddObjectRef(module, "ContextFunctions", m);
return 0;
}
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,17 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
extern "C" {
#include <Python.h>
}
/*---------------------------Python BPy_ContextFunctions visible prototypes-----------*/
int ContextFunctions_Init(PyObject *module);

View File

@@ -0,0 +1,825 @@
/* SPDX-FileCopyrightText: 2008-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_Convert.h"
#include "BPy_BBox.h"
#include "BPy_FrsMaterial.h"
#include "BPy_Id.h"
#include "BPy_IntegrationType.h"
#include "BPy_Interface0D.h"
#include "BPy_Interface1D.h"
#include "BPy_MediumType.h"
#include "BPy_Nature.h"
#include "BPy_SShape.h"
#include "BPy_StrokeAttribute.h"
#include "BPy_ViewShape.h"
#include "Interface0D/BPy_CurvePoint.h"
#include "Interface0D/BPy_SVertex.h"
#include "Interface0D/BPy_ViewVertex.h"
#include "Interface0D/CurvePoint/BPy_StrokeVertex.h"
#include "Interface0D/ViewVertex/BPy_NonTVertex.h"
#include "Interface0D/ViewVertex/BPy_TVertex.h"
#include "Interface1D/BPy_FEdge.h"
#include "Interface1D/BPy_Stroke.h"
#include "Interface1D/BPy_ViewEdge.h"
#include "Interface1D/Curve/BPy_Chain.h"
#include "Interface1D/FEdge/BPy_FEdgeSharp.h"
#include "Interface1D/FEdge/BPy_FEdgeSmooth.h"
#include "Iterator/BPy_AdjacencyIterator.h"
#include "Iterator/BPy_ChainPredicateIterator.h"
#include "Iterator/BPy_ChainSilhouetteIterator.h"
#include "Iterator/BPy_ChainingIterator.h"
#include "Iterator/BPy_CurvePointIterator.h"
#include "Iterator/BPy_Interface0DIterator.h"
#include "Iterator/BPy_SVertexIterator.h"
#include "Iterator/BPy_StrokeVertexIterator.h"
#include "Iterator/BPy_ViewEdgeIterator.h"
#include "Iterator/BPy_orientedViewEdgeIterator.h"
#include "../stroke/StrokeRep.h"
using namespace Freestyle;
using namespace Freestyle::Geometry;
///////////////////////////////////////////////////////////////////////////////////////////
//==============================
// C++ => Python
//==============================
PyObject *PyBool_from_bool(bool b)
{
return PyBool_FromLong(b ? 1 : 0);
}
PyObject *PyLong_subtype_new(PyTypeObject *ty, long value)
{
BLI_assert(ty->tp_basicsize == sizeof(PyLongObject));
PyLongObject *result = PyObject_NewVar(PyLongObject, ty, 1);
PyLongObject *value_py = (PyLongObject *)PyLong_FromLong(value);
memcpy(&result->long_value, &value_py->long_value, sizeof(result->long_value));
Py_DECREF(value_py);
return (PyObject *)result;
}
void PyLong_subtype_add_to_dict(PyObject *dict, PyTypeObject *ty, const char *attr, long value)
{
PyObject *result = PyLong_subtype_new(ty, value);
PyDict_SetItemString(dict, attr, result);
/* Owned by the dictionary. */
Py_DECREF(result);
}
PyObject *Vector_from_Vec2f(Vec2f &vec)
{
float vec_data[2]; // because vec->_coord is protected
vec_data[0] = vec.x();
vec_data[1] = vec.y();
return blender::Vector_CreatePyObject(vec_data, 2, nullptr);
}
PyObject *Vector_from_Vec3f(Vec3f &vec)
{
float vec_data[3]; // because vec->_coord is protected
vec_data[0] = vec.x();
vec_data[1] = vec.y();
vec_data[2] = vec.z();
return blender::Vector_CreatePyObject(vec_data, 3, nullptr);
}
PyObject *Vector_from_Vec3r(Vec3r &vec)
{
float vec_data[3]; // because vec->_coord is protected
vec_data[0] = vec.x();
vec_data[1] = vec.y();
vec_data[2] = vec.z();
return blender::Vector_CreatePyObject(vec_data, 3, nullptr);
}
PyObject *BPy_Id_from_Id(Id &id)
{
PyObject *py_id = Id_Type.tp_new(&Id_Type, nullptr, nullptr);
((BPy_Id *)py_id)->id = new Id(id.getFirst(), id.getSecond());
return py_id;
}
PyObject *Any_BPy_Interface0D_from_Interface0D(Interface0D &if0D)
{
if (typeid(if0D) == typeid(CurvePoint)) {
return BPy_CurvePoint_from_CurvePoint(dynamic_cast<CurvePoint &>(if0D));
}
if (typeid(if0D) == typeid(StrokeVertex)) {
return BPy_StrokeVertex_from_StrokeVertex(dynamic_cast<StrokeVertex &>(if0D));
}
if (typeid(if0D) == typeid(SVertex)) {
return BPy_SVertex_from_SVertex(dynamic_cast<SVertex &>(if0D));
}
if (typeid(if0D) == typeid(ViewVertex)) {
return BPy_ViewVertex_from_ViewVertex(dynamic_cast<ViewVertex &>(if0D));
}
if (typeid(if0D) == typeid(NonTVertex)) {
return BPy_NonTVertex_from_NonTVertex(dynamic_cast<NonTVertex &>(if0D));
}
if (typeid(if0D) == typeid(TVertex)) {
return BPy_TVertex_from_TVertex(dynamic_cast<TVertex &>(if0D));
}
if (typeid(if0D) == typeid(Interface0D)) {
return BPy_Interface0D_from_Interface0D(if0D);
}
string msg("unexpected type: " + if0D.getExactTypeName());
PyErr_SetString(PyExc_TypeError, msg.c_str());
return nullptr;
}
PyObject *Any_BPy_Interface1D_from_Interface1D(Interface1D &if1D)
{
if (typeid(if1D) == typeid(ViewEdge)) {
return BPy_ViewEdge_from_ViewEdge(dynamic_cast<ViewEdge &>(if1D));
}
if (typeid(if1D) == typeid(Chain)) {
return BPy_Chain_from_Chain(dynamic_cast<Chain &>(if1D));
}
if (typeid(if1D) == typeid(Stroke)) {
return BPy_Stroke_from_Stroke(dynamic_cast<Stroke &>(if1D));
}
if (typeid(if1D) == typeid(FEdgeSharp)) {
return BPy_FEdgeSharp_from_FEdgeSharp(dynamic_cast<FEdgeSharp &>(if1D));
}
if (typeid(if1D) == typeid(FEdgeSmooth)) {
return BPy_FEdgeSmooth_from_FEdgeSmooth(dynamic_cast<FEdgeSmooth &>(if1D));
}
if (typeid(if1D) == typeid(FEdge)) {
return BPy_FEdge_from_FEdge(dynamic_cast<FEdge &>(if1D));
}
if (typeid(if1D) == typeid(Interface1D)) {
return BPy_Interface1D_from_Interface1D(if1D);
}
string msg("unexpected type: " + if1D.getExactTypeName());
PyErr_SetString(PyExc_TypeError, msg.c_str());
return nullptr;
}
PyObject *Any_BPy_FEdge_from_FEdge(FEdge &fe)
{
if (typeid(fe) == typeid(FEdgeSharp)) {
return BPy_FEdgeSharp_from_FEdgeSharp(dynamic_cast<FEdgeSharp &>(fe));
}
if (typeid(fe) == typeid(FEdgeSmooth)) {
return BPy_FEdgeSmooth_from_FEdgeSmooth(dynamic_cast<FEdgeSmooth &>(fe));
}
if (typeid(fe) == typeid(FEdge)) {
return BPy_FEdge_from_FEdge(fe);
}
string msg("unexpected type: " + fe.getExactTypeName());
PyErr_SetString(PyExc_TypeError, msg.c_str());
return nullptr;
}
PyObject *Any_BPy_ViewVertex_from_ViewVertex(ViewVertex &vv)
{
if (typeid(vv) == typeid(NonTVertex)) {
return BPy_NonTVertex_from_NonTVertex(dynamic_cast<NonTVertex &>(vv));
}
if (typeid(vv) == typeid(TVertex)) {
return BPy_TVertex_from_TVertex(dynamic_cast<TVertex &>(vv));
}
if (typeid(vv) == typeid(ViewVertex)) {
return BPy_ViewVertex_from_ViewVertex(vv);
}
string msg("unexpected type: " + vv.getExactTypeName());
PyErr_SetString(PyExc_TypeError, msg.c_str());
return nullptr;
}
PyObject *BPy_Interface0D_from_Interface0D(Interface0D &if0D)
{
PyObject *py_if0D = Interface0D_Type.tp_new(&Interface0D_Type, nullptr, nullptr);
((BPy_Interface0D *)py_if0D)->if0D = &if0D;
((BPy_Interface0D *)py_if0D)->borrowed = true;
return py_if0D;
}
PyObject *BPy_Interface1D_from_Interface1D(Interface1D &if1D)
{
PyObject *py_if1D = Interface1D_Type.tp_new(&Interface1D_Type, nullptr, nullptr);
((BPy_Interface1D *)py_if1D)->if1D = &if1D;
((BPy_Interface1D *)py_if1D)->borrowed = true;
return py_if1D;
}
PyObject *BPy_SVertex_from_SVertex(SVertex &sv)
{
PyObject *py_sv = SVertex_Type.tp_new(&SVertex_Type, nullptr, nullptr);
((BPy_SVertex *)py_sv)->sv = &sv;
((BPy_SVertex *)py_sv)->py_if0D.if0D = ((BPy_SVertex *)py_sv)->sv;
((BPy_SVertex *)py_sv)->py_if0D.borrowed = true;
return py_sv;
}
PyObject *BPy_FEdgeSharp_from_FEdgeSharp(FEdgeSharp &fes)
{
PyObject *py_fe = FEdgeSharp_Type.tp_new(&FEdgeSharp_Type, nullptr, nullptr);
((BPy_FEdgeSharp *)py_fe)->fes = &fes;
((BPy_FEdgeSharp *)py_fe)->py_fe.fe = ((BPy_FEdgeSharp *)py_fe)->fes;
((BPy_FEdgeSharp *)py_fe)->py_fe.py_if1D.if1D = ((BPy_FEdgeSharp *)py_fe)->fes;
((BPy_FEdgeSharp *)py_fe)->py_fe.py_if1D.borrowed = true;
return py_fe;
}
PyObject *BPy_FEdgeSmooth_from_FEdgeSmooth(FEdgeSmooth &fes)
{
PyObject *py_fe = FEdgeSmooth_Type.tp_new(&FEdgeSmooth_Type, nullptr, nullptr);
((BPy_FEdgeSmooth *)py_fe)->fes = &fes;
((BPy_FEdgeSmooth *)py_fe)->py_fe.fe = ((BPy_FEdgeSmooth *)py_fe)->fes;
((BPy_FEdgeSmooth *)py_fe)->py_fe.py_if1D.if1D = ((BPy_FEdgeSmooth *)py_fe)->fes;
((BPy_FEdgeSmooth *)py_fe)->py_fe.py_if1D.borrowed = true;
return py_fe;
}
PyObject *BPy_FEdge_from_FEdge(FEdge &fe)
{
PyObject *py_fe = FEdge_Type.tp_new(&FEdge_Type, nullptr, nullptr);
((BPy_FEdge *)py_fe)->fe = &fe;
((BPy_FEdge *)py_fe)->py_if1D.if1D = ((BPy_FEdge *)py_fe)->fe;
((BPy_FEdge *)py_fe)->py_if1D.borrowed = true;
return py_fe;
}
PyObject *BPy_Nature_from_Nature(ushort n)
{
PyObject *args = PyTuple_New(1);
PyTuple_SET_ITEM(args, 0, PyLong_FromLong(n));
PyObject *py_n = Nature_Type.tp_new(&Nature_Type, args, nullptr);
Py_DECREF(args);
return py_n;
}
PyObject *BPy_Stroke_from_Stroke(Stroke &s)
{
PyObject *py_s = Stroke_Type.tp_new(&Stroke_Type, nullptr, nullptr);
((BPy_Stroke *)py_s)->s = &s;
((BPy_Stroke *)py_s)->py_if1D.if1D = ((BPy_Stroke *)py_s)->s;
((BPy_Stroke *)py_s)->py_if1D.borrowed = true;
return py_s;
}
PyObject *BPy_StrokeAttribute_from_StrokeAttribute(StrokeAttribute &sa)
{
PyObject *py_sa = StrokeAttribute_Type.tp_new(&StrokeAttribute_Type, nullptr, nullptr);
((BPy_StrokeAttribute *)py_sa)->sa = &sa;
((BPy_StrokeAttribute *)py_sa)->borrowed = true;
return py_sa;
}
PyObject *BPy_MediumType_from_MediumType(Stroke::MediumType n)
{
PyObject *args = PyTuple_New(1);
PyTuple_SET_ITEM(args, 0, PyLong_FromLong(n));
PyObject *py_mt = MediumType_Type.tp_new(&MediumType_Type, args, nullptr);
Py_DECREF(args);
return py_mt;
}
PyObject *BPy_StrokeVertex_from_StrokeVertex(StrokeVertex &sv)
{
PyObject *py_sv = StrokeVertex_Type.tp_new(&StrokeVertex_Type, nullptr, nullptr);
((BPy_StrokeVertex *)py_sv)->sv = &sv;
((BPy_StrokeVertex *)py_sv)->py_cp.cp = ((BPy_StrokeVertex *)py_sv)->sv;
((BPy_StrokeVertex *)py_sv)->py_cp.py_if0D.if0D = ((BPy_StrokeVertex *)py_sv)->sv;
((BPy_StrokeVertex *)py_sv)->py_cp.py_if0D.borrowed = true;
return py_sv;
}
PyObject *BPy_ViewVertex_from_ViewVertex(ViewVertex &vv)
{
PyObject *py_vv = ViewVertex_Type.tp_new(&ViewVertex_Type, nullptr, nullptr);
((BPy_ViewVertex *)py_vv)->vv = &vv;
((BPy_ViewVertex *)py_vv)->py_if0D.if0D = ((BPy_ViewVertex *)py_vv)->vv;
((BPy_ViewVertex *)py_vv)->py_if0D.borrowed = true;
return py_vv;
}
PyObject *BPy_NonTVertex_from_NonTVertex(NonTVertex &ntv)
{
PyObject *py_ntv = NonTVertex_Type.tp_new(&NonTVertex_Type, nullptr, nullptr);
((BPy_NonTVertex *)py_ntv)->ntv = &ntv;
((BPy_NonTVertex *)py_ntv)->py_vv.vv = ((BPy_NonTVertex *)py_ntv)->ntv;
((BPy_NonTVertex *)py_ntv)->py_vv.py_if0D.if0D = ((BPy_NonTVertex *)py_ntv)->ntv;
((BPy_NonTVertex *)py_ntv)->py_vv.py_if0D.borrowed = true;
return py_ntv;
}
PyObject *BPy_TVertex_from_TVertex(TVertex &tv)
{
PyObject *py_tv = TVertex_Type.tp_new(&TVertex_Type, nullptr, nullptr);
((BPy_TVertex *)py_tv)->tv = &tv;
((BPy_TVertex *)py_tv)->py_vv.vv = ((BPy_TVertex *)py_tv)->tv;
((BPy_TVertex *)py_tv)->py_vv.py_if0D.if0D = ((BPy_TVertex *)py_tv)->tv;
((BPy_TVertex *)py_tv)->py_vv.py_if0D.borrowed = true;
return py_tv;
}
PyObject *BPy_BBox_from_BBox(const BBox<Vec3r> &bb)
{
PyObject *py_bb = BBox_Type.tp_new(&BBox_Type, nullptr, nullptr);
((BPy_BBox *)py_bb)->bb = new BBox<Vec3r>(bb);
return py_bb;
}
PyObject *BPy_ViewEdge_from_ViewEdge(ViewEdge &ve)
{
PyObject *py_ve = ViewEdge_Type.tp_new(&ViewEdge_Type, nullptr, nullptr);
((BPy_ViewEdge *)py_ve)->ve = &ve;
((BPy_ViewEdge *)py_ve)->py_if1D.if1D = ((BPy_ViewEdge *)py_ve)->ve;
((BPy_ViewEdge *)py_ve)->py_if1D.borrowed = true;
return py_ve;
}
PyObject *BPy_Chain_from_Chain(Chain &c)
{
PyObject *py_c = Chain_Type.tp_new(&Chain_Type, nullptr, nullptr);
((BPy_Chain *)py_c)->c = &c;
((BPy_Chain *)py_c)->py_c.c = ((BPy_Chain *)py_c)->c;
((BPy_Chain *)py_c)->py_c.py_if1D.if1D = ((BPy_Chain *)py_c)->c;
((BPy_Chain *)py_c)->py_c.py_if1D.borrowed = true;
return py_c;
}
PyObject *BPy_SShape_from_SShape(SShape &ss)
{
PyObject *py_ss = SShape_Type.tp_new(&SShape_Type, nullptr, nullptr);
((BPy_SShape *)py_ss)->ss = &ss;
((BPy_SShape *)py_ss)->borrowed = true;
return py_ss;
}
PyObject *BPy_ViewShape_from_ViewShape(ViewShape &vs)
{
PyObject *py_vs = ViewShape_Type.tp_new(&ViewShape_Type, nullptr, nullptr);
((BPy_ViewShape *)py_vs)->vs = &vs;
((BPy_ViewShape *)py_vs)->borrowed = true;
((BPy_ViewShape *)py_vs)->py_ss = nullptr;
return py_vs;
}
PyObject *BPy_FrsMaterial_from_FrsMaterial(const FrsMaterial &m)
{
PyObject *py_m = FrsMaterial_Type.tp_new(&FrsMaterial_Type, nullptr, nullptr);
((BPy_FrsMaterial *)py_m)->m = new FrsMaterial(m);
return py_m;
}
PyObject *BPy_IntegrationType_from_IntegrationType(IntegrationType i)
{
PyObject *args = PyTuple_New(1);
PyTuple_SET_ITEM(args, 0, PyLong_FromLong(i));
PyObject *py_it = IntegrationType_Type.tp_new(&IntegrationType_Type, args, nullptr);
Py_DECREF(args);
return py_it;
}
PyObject *BPy_CurvePoint_from_CurvePoint(CurvePoint &cp)
{
PyObject *py_cp = CurvePoint_Type.tp_new(&CurvePoint_Type, nullptr, nullptr);
// CurvePointIterator::operator*() returns a reference of a class data
// member whose value is mutable upon iteration over different CurvePoints.
// It is likely that such a mutable reference is passed to this function,
// so that a new allocated CurvePoint instance is created here to avoid
// nasty bugs (cf. #41464).
((BPy_CurvePoint *)py_cp)->cp = new CurvePoint(cp);
((BPy_CurvePoint *)py_cp)->py_if0D.if0D = ((BPy_CurvePoint *)py_cp)->cp;
((BPy_CurvePoint *)py_cp)->py_if0D.borrowed = false;
return py_cp;
}
PyObject *BPy_directedViewEdge_from_directedViewEdge(ViewVertex::directedViewEdge &dve)
{
PyObject *py_dve = PyTuple_New(2);
PyTuple_SET_ITEMS(
py_dve, BPy_ViewEdge_from_ViewEdge(*(dve.first)), PyBool_from_bool(dve.second));
return py_dve;
}
//==============================
// Iterators
//==============================
PyObject *BPy_AdjacencyIterator_from_AdjacencyIterator(AdjacencyIterator &a_it)
{
PyObject *py_a_it = AdjacencyIterator_Type.tp_new(&AdjacencyIterator_Type, nullptr, nullptr);
((BPy_AdjacencyIterator *)py_a_it)->a_it = new AdjacencyIterator(a_it);
((BPy_AdjacencyIterator *)py_a_it)->py_it.it = ((BPy_AdjacencyIterator *)py_a_it)->a_it;
((BPy_AdjacencyIterator *)py_a_it)->at_start = true;
return py_a_it;
}
PyObject *BPy_Interface0DIterator_from_Interface0DIterator(Interface0DIterator &if0D_it,
bool reversed)
{
PyObject *py_if0D_it = Interface0DIterator_Type.tp_new(
&Interface0DIterator_Type, nullptr, nullptr);
((BPy_Interface0DIterator *)py_if0D_it)->if0D_it = new Interface0DIterator(if0D_it);
((BPy_Interface0DIterator *)py_if0D_it)->py_it.it =
((BPy_Interface0DIterator *)py_if0D_it)->if0D_it;
((BPy_Interface0DIterator *)py_if0D_it)->at_start = true;
((BPy_Interface0DIterator *)py_if0D_it)->reversed = reversed;
return py_if0D_it;
}
PyObject *BPy_CurvePointIterator_from_CurvePointIterator(CurveInternal::CurvePointIterator &cp_it)
{
PyObject *py_cp_it = CurvePointIterator_Type.tp_new(&CurvePointIterator_Type, nullptr, nullptr);
((BPy_CurvePointIterator *)py_cp_it)->cp_it = new CurveInternal::CurvePointIterator(cp_it);
((BPy_CurvePointIterator *)py_cp_it)->py_it.it = ((BPy_CurvePointIterator *)py_cp_it)->cp_it;
return py_cp_it;
}
PyObject *BPy_StrokeVertexIterator_from_StrokeVertexIterator(
StrokeInternal::StrokeVertexIterator &sv_it, bool reversed)
{
PyObject *py_sv_it = StrokeVertexIterator_Type.tp_new(
&StrokeVertexIterator_Type, nullptr, nullptr);
((BPy_StrokeVertexIterator *)py_sv_it)->sv_it = new StrokeInternal::StrokeVertexIterator(sv_it);
((BPy_StrokeVertexIterator *)py_sv_it)->py_it.it = ((BPy_StrokeVertexIterator *)py_sv_it)->sv_it;
((BPy_StrokeVertexIterator *)py_sv_it)->at_start = true;
((BPy_StrokeVertexIterator *)py_sv_it)->reversed = reversed;
return py_sv_it;
}
PyObject *BPy_SVertexIterator_from_SVertexIterator(ViewEdgeInternal::SVertexIterator &sv_it)
{
PyObject *py_sv_it = SVertexIterator_Type.tp_new(&SVertexIterator_Type, nullptr, nullptr);
((BPy_SVertexIterator *)py_sv_it)->sv_it = new ViewEdgeInternal::SVertexIterator(sv_it);
((BPy_SVertexIterator *)py_sv_it)->py_it.it = ((BPy_SVertexIterator *)py_sv_it)->sv_it;
return py_sv_it;
}
PyObject *BPy_orientedViewEdgeIterator_from_orientedViewEdgeIterator(
ViewVertexInternal::orientedViewEdgeIterator &ove_it, bool reversed)
{
PyObject *py_ove_it = orientedViewEdgeIterator_Type.tp_new(
&orientedViewEdgeIterator_Type, nullptr, nullptr);
((BPy_orientedViewEdgeIterator *)py_ove_it)->ove_it =
new ViewVertexInternal::orientedViewEdgeIterator(ove_it);
((BPy_orientedViewEdgeIterator *)py_ove_it)->py_it.it =
((BPy_orientedViewEdgeIterator *)py_ove_it)->ove_it;
((BPy_orientedViewEdgeIterator *)py_ove_it)->at_start = true;
((BPy_orientedViewEdgeIterator *)py_ove_it)->reversed = reversed;
return py_ove_it;
}
PyObject *BPy_ViewEdgeIterator_from_ViewEdgeIterator(ViewEdgeInternal::ViewEdgeIterator &ve_it)
{
PyObject *py_ve_it = ViewEdgeIterator_Type.tp_new(&ViewEdgeIterator_Type, nullptr, nullptr);
((BPy_ViewEdgeIterator *)py_ve_it)->ve_it = new ViewEdgeInternal::ViewEdgeIterator(ve_it);
((BPy_ViewEdgeIterator *)py_ve_it)->py_it.it = ((BPy_ViewEdgeIterator *)py_ve_it)->ve_it;
return py_ve_it;
}
PyObject *BPy_ChainingIterator_from_ChainingIterator(ChainingIterator &c_it)
{
PyObject *py_c_it = ChainingIterator_Type.tp_new(&ChainingIterator_Type, nullptr, nullptr);
((BPy_ChainingIterator *)py_c_it)->c_it = new ChainingIterator(c_it);
((BPy_ChainingIterator *)py_c_it)->py_ve_it.py_it.it = ((BPy_ChainingIterator *)py_c_it)->c_it;
return py_c_it;
}
PyObject *BPy_ChainPredicateIterator_from_ChainPredicateIterator(ChainPredicateIterator &cp_it)
{
PyObject *py_cp_it = ChainPredicateIterator_Type.tp_new(
&ChainPredicateIterator_Type, nullptr, nullptr);
((BPy_ChainPredicateIterator *)py_cp_it)->cp_it = new ChainPredicateIterator(cp_it);
((BPy_ChainPredicateIterator *)py_cp_it)->py_c_it.py_ve_it.py_it.it =
((BPy_ChainPredicateIterator *)py_cp_it)->cp_it;
return py_cp_it;
}
PyObject *BPy_ChainSilhouetteIterator_from_ChainSilhouetteIterator(ChainSilhouetteIterator &cs_it)
{
PyObject *py_cs_it = ChainSilhouetteIterator_Type.tp_new(
&ChainSilhouetteIterator_Type, nullptr, nullptr);
((BPy_ChainSilhouetteIterator *)py_cs_it)->cs_it = new ChainSilhouetteIterator(cs_it);
((BPy_ChainSilhouetteIterator *)py_cs_it)->py_c_it.py_ve_it.py_it.it =
((BPy_ChainSilhouetteIterator *)py_cs_it)->cs_it;
return py_cs_it;
}
//==============================
// Python => C++
//==============================
bool bool_from_PyBool(PyObject *b)
{
return PyObject_IsTrue(b) != 0;
}
IntegrationType IntegrationType_from_BPy_IntegrationType(PyObject *obj)
{
return static_cast<IntegrationType>(PyLong_AsLong(obj));
}
Stroke::MediumType MediumType_from_BPy_MediumType(PyObject *obj)
{
return static_cast<Stroke::MediumType>(PyLong_AsLong(obj));
}
Nature::EdgeNature EdgeNature_from_BPy_Nature(PyObject *obj)
{
return static_cast<Nature::EdgeNature>(PyLong_AsLong(obj));
}
bool Vec2f_ptr_from_PyObject(PyObject *obj, Vec2f &vec)
{
if (Vec2f_ptr_from_Vector(obj, vec)) {
return true;
}
if (Vec2f_ptr_from_PyList(obj, vec)) {
return true;
}
if (Vec2f_ptr_from_PyTuple(obj, vec)) {
return true;
}
return false;
}
bool Vec3f_ptr_from_PyObject(PyObject *obj, Vec3f &vec)
{
if (Vec3f_ptr_from_Vector(obj, vec)) {
return true;
}
if (Vec3f_ptr_from_Color(obj, vec)) {
return true;
}
if (Vec3f_ptr_from_PyList(obj, vec)) {
return true;
}
if (Vec3f_ptr_from_PyTuple(obj, vec)) {
return true;
}
return false;
}
bool Vec3r_ptr_from_PyObject(PyObject *obj, Vec3r &vec)
{
if (Vec3r_ptr_from_Vector(obj, vec)) {
return true;
}
if (Vec3r_ptr_from_Color(obj, vec)) {
return true;
}
if (Vec3r_ptr_from_PyList(obj, vec)) {
return true;
}
if (Vec3r_ptr_from_PyTuple(obj, vec)) {
return true;
}
return false;
}
bool Vec2f_ptr_from_Vector(PyObject *obj, Vec2f &vec)
{
using namespace blender;
if (!VectorObject_Check(obj) || ((VectorObject *)obj)->vec_num != 2) {
return false;
}
if (BaseMath_ReadCallback((blender::BaseMathObject *)obj) == -1) {
return false;
}
vec[0] = ((VectorObject *)obj)->vec[0];
vec[1] = ((VectorObject *)obj)->vec[1];
return true;
}
bool Vec3f_ptr_from_Vector(PyObject *obj, Vec3f &vec)
{
using namespace blender;
if (!VectorObject_Check(obj) || ((VectorObject *)obj)->vec_num != 3) {
return false;
}
if (BaseMath_ReadCallback((blender::BaseMathObject *)obj) == -1) {
return false;
}
vec[0] = ((VectorObject *)obj)->vec[0];
vec[1] = ((VectorObject *)obj)->vec[1];
vec[2] = ((VectorObject *)obj)->vec[2];
return true;
}
bool Vec3r_ptr_from_Vector(PyObject *obj, Vec3r &vec)
{
using namespace blender;
if (!VectorObject_Check(obj) || ((VectorObject *)obj)->vec_num != 3) {
return false;
}
if (BaseMath_ReadCallback((blender::BaseMathObject *)obj) == -1) {
return false;
}
vec[0] = ((VectorObject *)obj)->vec[0];
vec[1] = ((VectorObject *)obj)->vec[1];
vec[2] = ((VectorObject *)obj)->vec[2];
return true;
}
bool Vec3f_ptr_from_Color(PyObject *obj, Vec3f &vec)
{
using namespace blender;
if (!ColorObject_Check(obj)) {
return false;
}
if (BaseMath_ReadCallback((blender::BaseMathObject *)obj) == -1) {
return false;
}
vec[0] = ((ColorObject *)obj)->col[0];
vec[1] = ((ColorObject *)obj)->col[1];
vec[2] = ((ColorObject *)obj)->col[2];
return true;
}
bool Vec3r_ptr_from_Color(PyObject *obj, Vec3r &vec)
{
using namespace blender;
if (!ColorObject_Check(obj)) {
return false;
}
if (BaseMath_ReadCallback((blender::BaseMathObject *)obj) == -1) {
return false;
}
vec[0] = ((ColorObject *)obj)->col[0];
vec[1] = ((ColorObject *)obj)->col[1];
vec[2] = ((ColorObject *)obj)->col[2];
return true;
}
static bool float_array_from_PyList(PyObject *obj, float *v, int n)
{
for (int i = 0; i < n; i++) {
v[i] = PyFloat_AsDouble(PyList_GET_ITEM(obj, i));
if (v[i] == -1.0f && PyErr_Occurred()) {
PyErr_SetString(PyExc_TypeError, "list elements must be a number");
return false;
}
}
return true;
}
bool Vec2f_ptr_from_PyList(PyObject *obj, Vec2f &vec)
{
float v[2];
if (!PyList_Check(obj) || PyList_GET_SIZE(obj) != 2) {
return false;
}
if (!float_array_from_PyList(obj, v, 2)) {
return false;
}
vec[0] = v[0];
vec[1] = v[1];
return true;
}
bool Vec3f_ptr_from_PyList(PyObject *obj, Vec3f &vec)
{
float v[3];
if (!PyList_Check(obj) || PyList_GET_SIZE(obj) != 3) {
return false;
}
if (!float_array_from_PyList(obj, v, 3)) {
return false;
}
vec[0] = v[0];
vec[1] = v[1];
vec[2] = v[2];
return true;
}
bool Vec3r_ptr_from_PyList(PyObject *obj, Vec3r &vec)
{
float v[3];
if (!PyList_Check(obj) || PyList_GET_SIZE(obj) != 3) {
return false;
}
if (!float_array_from_PyList(obj, v, 3)) {
return false;
}
vec[0] = v[0];
vec[1] = v[1];
vec[2] = v[2];
return true;
}
static bool float_array_from_PyTuple(PyObject *obj, float *v, int n)
{
for (int i = 0; i < n; i++) {
v[i] = PyFloat_AsDouble(PyTuple_GET_ITEM(obj, i));
if (v[i] == -1.0f && PyErr_Occurred()) {
PyErr_SetString(PyExc_TypeError, "tuple elements must be a number");
return false;
}
}
return true;
}
bool Vec2f_ptr_from_PyTuple(PyObject *obj, Vec2f &vec)
{
float v[2];
if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 2) {
return false;
}
if (!float_array_from_PyTuple(obj, v, 2)) {
return false;
}
vec[0] = v[0];
vec[1] = v[1];
return true;
}
bool Vec3f_ptr_from_PyTuple(PyObject *obj, Vec3f &vec)
{
float v[3];
if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 3) {
return false;
}
if (!float_array_from_PyTuple(obj, v, 3)) {
return false;
}
vec[0] = v[0];
vec[1] = v[1];
vec[2] = v[2];
return true;
}
bool Vec3r_ptr_from_PyTuple(PyObject *obj, Vec3r &vec)
{
float v[3];
if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 3) {
return false;
}
if (!float_array_from_PyTuple(obj, v, 3)) {
return false;
}
vec[0] = v[0];
vec[1] = v[1];
vec[2] = v[2];
return true;
}
// helpers for argument parsing
bool float_array_from_PyObject(PyObject *obj, float *v, int n)
{
using namespace blender;
if (VectorObject_Check(obj) && ((VectorObject *)obj)->vec_num == n) {
if (BaseMath_ReadCallback((blender::BaseMathObject *)obj) == -1) {
return false;
}
for (int i = 0; i < n; i++) {
v[i] = ((VectorObject *)obj)->vec[i];
}
return true;
}
if (ColorObject_Check(obj) && n == 3) {
if (BaseMath_ReadCallback((blender::BaseMathObject *)obj) == -1) {
return false;
}
for (int i = 0; i < n; i++) {
v[i] = ((ColorObject *)obj)->col[i];
}
return true;
}
if (PyList_Check(obj) && PyList_GET_SIZE(obj) == n) {
return float_array_from_PyList(obj, v, n);
}
if (PyTuple_Check(obj) && PyTuple_GET_SIZE(obj) == n) {
return float_array_from_PyTuple(obj, v, n);
}
return false;
}
int convert_v4(PyObject *obj, void *v)
{
return blender::mathutils_array_parse((float *)v, 4, 4, obj, "Error parsing 4D vector");
}
int convert_v3(PyObject *obj, void *v)
{
return blender::mathutils_array_parse((float *)v, 3, 3, obj, "Error parsing 3D vector");
}
int convert_v2(PyObject *obj, void *v)
{
return blender::mathutils_array_parse((float *)v, 2, 2, obj, "Error parsing 2D vector");
}
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,163 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
extern "C" {
#include <Python.h>
}
#include <typeinfo>
#include "../geometry/Geom.h"
// BBox
#include "../geometry/BBox.h"
// FEdge, FEdgeSharp, FEdgeSmooth, SShape, SVertex, FEdgeInternal::SVertexIterator
#include "../view_map/Silhouette.h"
// Id
#include "../system/Id.h"
// Interface0D, Interface0DIteratorNested, Interface0DIterator
#include "../view_map/Interface0D.h"
// Interface1D
#include "../view_map/Interface1D.h"
// FrsMaterial
#include "../scene_graph/FrsMaterial.h"
// Nature::VertexNature, Nature::EdgeNature
#include "../winged_edge/Nature.h"
// Stroke, StrokeAttribute, StrokeVertex
#include "../stroke/Stroke.h"
// NonTVertex, TVertex, ViewEdge, ViewMap, ViewShape, ViewVertex
#include "../view_map/ViewMap.h"
// CurvePoint, Curve
#include "../stroke/Curve.h"
// Chain
#include "../stroke/Chain.h"
//====== ITERATORS
// AdjacencyIterator, ChainingIterator, ChainSilhouetteIterator, ChainPredicateIterator
#include "../stroke/ChainingIterators.h"
// ViewVertexInternal::orientedViewEdgeIterator
// ViewEdgeInternal::SVertexIterator
// ViewEdgeInternal::ViewEdgeIterator
#include "../view_map/ViewMapIterators.h"
// StrokeInternal::StrokeVertexIterator
#include "../stroke/StrokeIterators.h"
// CurveInternal::CurvePointIterator
#include "../stroke/CurveIterators.h"
///////////////////////////////////////////////////////////////////////////////////////////
#include "generic/python_utildefines.hh"
#include "mathutils/mathutils.hh"
//==============================
// C++ => Python
//==============================
PyObject *PyLong_subtype_new(PyTypeObject *ty, long value);
void PyLong_subtype_add_to_dict(PyObject *dict, PyTypeObject *ty, const char *attr, long value);
PyObject *PyBool_from_bool(bool b);
PyObject *Vector_from_Vec2f(Freestyle::Geometry::Vec2f &v);
PyObject *Vector_from_Vec3f(Freestyle::Geometry::Vec3f &v);
PyObject *Vector_from_Vec3r(Freestyle::Geometry::Vec3r &v);
PyObject *Any_BPy_Interface0D_from_Interface0D(Freestyle::Interface0D &if0D);
PyObject *Any_BPy_Interface1D_from_Interface1D(Freestyle::Interface1D &if1D);
PyObject *Any_BPy_FEdge_from_FEdge(Freestyle::FEdge &fe);
PyObject *Any_BPy_ViewVertex_from_ViewVertex(Freestyle::ViewVertex &vv);
PyObject *BPy_BBox_from_BBox(const Freestyle::BBox<Freestyle::Geometry::Vec3r> &bb);
PyObject *BPy_CurvePoint_from_CurvePoint(Freestyle::CurvePoint &cp);
PyObject *BPy_directedViewEdge_from_directedViewEdge(Freestyle::ViewVertex::directedViewEdge &dve);
PyObject *BPy_FEdge_from_FEdge(Freestyle::FEdge &fe);
PyObject *BPy_FEdgeSharp_from_FEdgeSharp(Freestyle::FEdgeSharp &fes);
PyObject *BPy_FEdgeSmooth_from_FEdgeSmooth(Freestyle::FEdgeSmooth &fes);
PyObject *BPy_Id_from_Id(Freestyle::Id &id);
PyObject *BPy_Interface0D_from_Interface0D(Freestyle::Interface0D &if0D);
PyObject *BPy_Interface1D_from_Interface1D(Freestyle::Interface1D &if1D);
PyObject *BPy_IntegrationType_from_IntegrationType(Freestyle::IntegrationType i);
PyObject *BPy_FrsMaterial_from_FrsMaterial(const Freestyle::FrsMaterial &m);
PyObject *BPy_Nature_from_Nature(ushort n);
PyObject *BPy_MediumType_from_MediumType(Freestyle::Stroke::MediumType n);
PyObject *BPy_SShape_from_SShape(Freestyle::SShape &ss);
PyObject *BPy_Stroke_from_Stroke(Freestyle::Stroke &s);
PyObject *BPy_StrokeAttribute_from_StrokeAttribute(Freestyle::StrokeAttribute &sa);
PyObject *BPy_StrokeVertex_from_StrokeVertex(Freestyle::StrokeVertex &sv);
PyObject *BPy_SVertex_from_SVertex(Freestyle::SVertex &sv);
PyObject *BPy_ViewVertex_from_ViewVertex(Freestyle::ViewVertex &vv);
PyObject *BPy_NonTVertex_from_NonTVertex(Freestyle::NonTVertex &ntv);
PyObject *BPy_TVertex_from_TVertex(Freestyle::TVertex &tv);
PyObject *BPy_ViewEdge_from_ViewEdge(Freestyle::ViewEdge &ve);
PyObject *BPy_Chain_from_Chain(Freestyle::Chain &c);
PyObject *BPy_ViewShape_from_ViewShape(Freestyle::ViewShape &vs);
PyObject *BPy_AdjacencyIterator_from_AdjacencyIterator(Freestyle::AdjacencyIterator &a_it);
PyObject *BPy_Interface0DIterator_from_Interface0DIterator(Freestyle::Interface0DIterator &if0D_it,
bool reversed);
PyObject *BPy_CurvePointIterator_from_CurvePointIterator(
Freestyle::CurveInternal::CurvePointIterator &cp_it);
PyObject *BPy_StrokeVertexIterator_from_StrokeVertexIterator(
Freestyle::StrokeInternal::StrokeVertexIterator &sv_it, bool reversed);
PyObject *BPy_SVertexIterator_from_SVertexIterator(
Freestyle::ViewEdgeInternal::SVertexIterator &sv_it);
PyObject *BPy_orientedViewEdgeIterator_from_orientedViewEdgeIterator(
Freestyle::ViewVertexInternal::orientedViewEdgeIterator &ove_it, bool reversed);
PyObject *BPy_ViewEdgeIterator_from_ViewEdgeIterator(
Freestyle::ViewEdgeInternal::ViewEdgeIterator &ve_it);
PyObject *BPy_ChainingIterator_from_ChainingIterator(Freestyle::ChainingIterator &c_it);
PyObject *BPy_ChainPredicateIterator_from_ChainPredicateIterator(
Freestyle::ChainPredicateIterator &cp_it);
PyObject *BPy_ChainSilhouetteIterator_from_ChainSilhouetteIterator(
Freestyle::ChainSilhouetteIterator &cs_it);
//==============================
// Python => C++
//==============================
bool bool_from_PyBool(PyObject *b);
Freestyle::IntegrationType IntegrationType_from_BPy_IntegrationType(PyObject *obj);
Freestyle::Stroke::MediumType MediumType_from_BPy_MediumType(PyObject *obj);
Freestyle::Nature::EdgeNature EdgeNature_from_BPy_Nature(PyObject *obj);
bool Vec2f_ptr_from_PyObject(PyObject *obj, Freestyle::Geometry::Vec2f &vec);
bool Vec3f_ptr_from_PyObject(PyObject *obj, Freestyle::Geometry::Vec3f &vec);
bool Vec3r_ptr_from_PyObject(PyObject *obj, Freestyle::Geometry::Vec3r &vec);
bool Vec2f_ptr_from_Vector(PyObject *obj, Freestyle::Geometry::Vec2f &vec);
bool Vec3f_ptr_from_Vector(PyObject *obj, Freestyle::Geometry::Vec3f &vec);
bool Vec3r_ptr_from_Vector(PyObject *obj, Freestyle::Geometry::Vec3r &vec);
bool Vec3f_ptr_from_Color(PyObject *obj, Freestyle::Geometry::Vec3f &vec);
bool Vec3r_ptr_from_Color(PyObject *obj, Freestyle::Geometry::Vec3r &vec);
bool Vec2f_ptr_from_PyList(PyObject *obj, Freestyle::Geometry::Vec2f &vec);
bool Vec3f_ptr_from_PyList(PyObject *obj, Freestyle::Geometry::Vec3f &vec);
bool Vec3r_ptr_from_PyList(PyObject *obj, Freestyle::Geometry::Vec3r &vec);
bool Vec2f_ptr_from_PyTuple(PyObject *obj, Freestyle::Geometry::Vec2f &vec);
bool Vec3f_ptr_from_PyTuple(PyObject *obj, Freestyle::Geometry::Vec3f &vec);
bool Vec3r_ptr_from_PyTuple(PyObject *obj, Freestyle::Geometry::Vec3r &vec);
bool float_array_from_PyObject(PyObject *obj, float *v, int n);
int convert_v4(PyObject *obj, void *v);
int convert_v3(PyObject *obj, void *v);
int convert_v2(PyObject *obj, void *v);
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,612 @@
/* SPDX-FileCopyrightText: 2008-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_Freestyle.h"
#include "BPy_BBox.h"
#include "BPy_BinaryPredicate0D.h"
#include "BPy_BinaryPredicate1D.h"
#include "BPy_ContextFunctions.h"
#include "BPy_Convert.h"
#include "BPy_FrsMaterial.h"
#include "BPy_FrsNoise.h"
#include "BPy_Id.h"
#include "BPy_IntegrationType.h"
#include "BPy_Interface0D.h"
#include "BPy_Interface1D.h"
#include "BPy_Iterator.h"
#include "BPy_MediumType.h"
#include "BPy_Nature.h"
#include "BPy_Operators.h"
#include "BPy_SShape.h"
#include "BPy_StrokeAttribute.h"
#include "BPy_StrokeShader.h"
#include "BPy_UnaryFunction0D.h"
#include "BPy_UnaryFunction1D.h"
#include "BPy_UnaryPredicate0D.h"
#include "BPy_UnaryPredicate1D.h"
#include "BPy_ViewMap.h"
#include "BPy_ViewShape.h"
#include "BKE_appdir.hh"
#include "DNA_scene_types.h"
#include "FRS_freestyle.h"
#include "RNA_access.hh"
#include "RNA_prototypes.hh"
#include "bpy_rna.hh" /* pyrna_struct_CreatePyObject() */
#include "../generic/py_capi_utils.hh" /* #PyC_UnicodeFromBytes */
#include "BKE_colorband.hh" /* BKE_colorband_evaluate() */
#include "BKE_colortools.hh" /* BKE_curvemapping_evaluateF() */
#include "BKE_material.hh" /* ramp_blend() */
///////////////////////////////////////////////////////////////////////////////////////////
//------------------------ MODULE FUNCTIONS ----------------------------------
PyDoc_STRVAR(
/* Wrap. */
Freestyle_getCurrentScene___doc__,
".. function:: getCurrentScene()\n"
"\n"
" Returns the current scene.\n"
"\n"
" :return: The current scene.\n"
" :rtype: :class:`bpy.types.Scene`\n");
static PyObject *Freestyle_getCurrentScene(PyObject * /*self*/)
{
blender::Scene *scene = blender::g_freestyle.scene;
if (!scene) {
PyErr_SetString(PyExc_TypeError, "current scene not available");
return nullptr;
}
blender::PointerRNA ptr_scene = RNA_pointer_create_discrete(
&scene->id, blender::RNA_Scene, scene);
return pyrna_struct_CreatePyObject(&ptr_scene);
}
#include "DNA_material_types.h"
static int ramp_blend_type(const char *type)
{
if (STREQ(type, "MIX")) {
return blender::MA_RAMP_BLEND;
}
if (STREQ(type, "ADD")) {
return blender::MA_RAMP_ADD;
}
if (STREQ(type, "MULTIPLY")) {
return blender::MA_RAMP_MULT;
}
if (STREQ(type, "SUBTRACT")) {
return blender::MA_RAMP_SUB;
}
if (STREQ(type, "SCREEN")) {
return blender::MA_RAMP_SCREEN;
}
if (STREQ(type, "DIVIDE")) {
return blender::MA_RAMP_DIV;
}
if (STREQ(type, "DIFFERENCE")) {
return blender::MA_RAMP_DIFF;
}
if (STREQ(type, "EXCLUSION")) {
return blender::MA_RAMP_EXCLUSION;
}
if (STREQ(type, "DARKEN")) {
return blender::MA_RAMP_DARK;
}
if (STREQ(type, "LIGHTEN")) {
return blender::MA_RAMP_LIGHT;
}
if (STREQ(type, "OVERLAY")) {
return blender::MA_RAMP_OVERLAY;
}
if (STREQ(type, "DODGE")) {
return blender::MA_RAMP_DODGE;
}
if (STREQ(type, "BURN")) {
return blender::MA_RAMP_BURN;
}
if (STREQ(type, "HUE")) {
return blender::MA_RAMP_HUE;
}
if (STREQ(type, "SATURATION")) {
return blender::MA_RAMP_SAT;
}
if (STREQ(type, "VALUE")) {
return blender::MA_RAMP_VAL;
}
if (STREQ(type, "COLOR")) {
return blender::MA_RAMP_COLOR;
}
if (STREQ(type, "SOFT_LIGHT")) {
return blender::MA_RAMP_SOFT;
}
if (STREQ(type, "LINEAR_LIGHT")) {
return blender::MA_RAMP_LINEAR;
}
return -1;
}
PyDoc_STRVAR(
/* Wrap. */
Freestyle_blendRamp___doc__,
".. function:: blendRamp(type, color1, fac, color2)\n"
"\n"
" Blend two colors according to a ramp blend type.\n"
"\n"
" :param type: Ramp blend type.\n"
" :type type: int\n"
" :param color1: 1st color.\n"
" :type color1: :class:`mathutils.Vector` | tuple[float, float, float] | list[float]\n"
" :param fac: Blend factor.\n"
" :type fac: float\n"
" :param color2: 1st color.\n"
" :type color2: :class:`mathutils.Vector` | tuple[float, float, float] | list[float]\n"
" :return: Blended color in RGB format.\n"
" :rtype: :class:`mathutils.Vector`\n");
static PyObject *Freestyle_blendRamp(PyObject * /*self*/, PyObject *args)
{
PyObject *obj1, *obj2;
char *s;
int type;
float a[4], fac, b[4];
if (!PyArg_ParseTuple(args, "sOfO", &s, &obj1, &fac, &obj2)) {
return nullptr;
}
type = ramp_blend_type(s);
if (type < 0) {
PyErr_SetString(PyExc_TypeError, "argument 1 is an unknown ramp blend type");
return nullptr;
}
if (blender::mathutils_array_parse(a,
3,
3,
obj1,
"argument 2 must be a 3D vector "
"(either a tuple/list of 3 elements or Vector)") == -1)
{
return nullptr;
}
if (blender::mathutils_array_parse(b,
3,
3,
obj2,
"argument 4 must be a 3D vector "
"(either a tuple/list of 3 elements or Vector)") == -1)
{
return nullptr;
}
blender::ramp_blend(type, a, fac, b);
return blender::Vector_CreatePyObject(a, 3, nullptr);
}
PyDoc_STRVAR(
/* Wrap. */
Freestyle_evaluateColorRamp___doc__,
".. function:: evaluateColorRamp(ramp, in)\n"
"\n"
" Evaluate a color ramp at a point in the interval 0 to 1.\n"
"\n"
" :param ramp: Color ramp object.\n"
" :type ramp: :class:`bpy.types.ColorRamp`\n"
" :param in: Value in the interval 0 to 1.\n"
" :type in: float\n"
" :return: color in RGBA format.\n"
" :rtype: :class:`mathutils.Vector`\n");
static PyObject *Freestyle_evaluateColorRamp(PyObject * /*self*/, PyObject *args)
{
blender::BPy_StructRNA *py_srna;
blender::ColorBand *coba;
float in, out[4];
if (!PyArg_ParseTuple(args, "O!f", &blender::pyrna_struct_Type, &py_srna, &in)) {
return nullptr;
}
if (!RNA_struct_is_a(py_srna->ptr->type, blender::RNA_ColorRamp)) {
PyErr_SetString(PyExc_TypeError, "1st argument is not a ColorRamp object");
return nullptr;
}
coba = (blender::ColorBand *)py_srna->ptr->data;
if (!BKE_colorband_evaluate(coba, in, out)) {
PyErr_SetString(PyExc_ValueError, "failed to evaluate the color ramp");
return nullptr;
}
return blender::Vector_CreatePyObject(out, 4, nullptr);
}
#include "DNA_color_types.h"
PyDoc_STRVAR(
/* Wrap. */
Freestyle_evaluateCurveMappingF___doc__,
".. function:: evaluateCurveMappingF(cumap, cur, value)\n"
"\n"
" Evaluate a curve mapping at a point in the interval 0 to 1.\n"
"\n"
" :param cumap: Curve mapping object.\n"
" :type cumap: :class:`bpy.types.CurveMapping`\n"
" :param cur: Index of the curve to be used (0 <= cur <= 3).\n"
" :type cur: int\n"
" :param value: Input value in the interval 0 to 1.\n"
" :type value: float\n"
" :return: Mapped output value.\n"
" :rtype: float\n");
static PyObject *Freestyle_evaluateCurveMappingF(PyObject * /*self*/, PyObject *args)
{
blender::BPy_StructRNA *py_srna;
blender::CurveMapping *cumap;
int cur;
float value;
if (!PyArg_ParseTuple(args, "O!if", &blender::pyrna_struct_Type, &py_srna, &cur, &value)) {
return nullptr;
}
if (!RNA_struct_is_a(py_srna->ptr->type, blender::RNA_CurveMapping)) {
PyErr_SetString(PyExc_TypeError, "1st argument is not a CurveMapping object");
return nullptr;
}
if (cur < 0 || cur > 3) {
PyErr_SetString(PyExc_ValueError, "2nd argument is out of range");
return nullptr;
}
cumap = (blender::CurveMapping *)py_srna->ptr->data;
BKE_curvemapping_init(cumap);
/* disable extrapolation if enabled */
if (cumap->flag & blender::CUMA_EXTEND_EXTRAPOLATE) {
cumap->flag &= ~blender::CUMA_EXTEND_EXTRAPOLATE;
BKE_curvemapping_changed(cumap, false);
}
return PyFloat_FromDouble(BKE_curvemapping_evaluateF(cumap, cur, value));
}
/*-----------------------Freestyle module docstring----------------------------*/
PyDoc_STRVAR(
/* Force wrapped line. */
module_docstring,
"This module provides classes for defining line drawing rules (such as\n"
"predicates, functions, chaining iterators, and stroke shaders), as well\n"
"as helper functions for style module writing.\n"
"\n"
"Class hierarchy:\n"
"\n"
"- :class:`BBox`\n"
"- :class:`BinaryPredicate0D`\n"
"- :class:`BinaryPredicate1D`\n"
"\n"
" - :class:`FalseBP1D`\n"
" - :class:`Length2DBP1D`\n"
" - :class:`SameShapeIdBP1D`\n"
" - :class:`TrueBP1D`\n"
" - :class:`ViewMapGradientNormBP1D`\n"
"\n"
"- :class:`Id`\n"
"- :class:`Interface0D`\n"
"\n"
" - :class:`CurvePoint`\n"
"\n"
" - :class:`StrokeVertex`\n"
"\n"
" - :class:`SVertex`\n"
" - :class:`ViewVertex`\n"
"\n"
" - :class:`NonTVertex`\n"
" - :class:`TVertex`\n"
"\n"
"- :class:`Interface1D`\n"
"\n"
" - :class:`Curve`\n"
"\n"
" - :class:`Chain`\n"
"\n"
" - :class:`FEdge`\n"
"\n"
" - :class:`FEdgeSharp`\n"
" - :class:`FEdgeSmooth`\n"
"\n"
" - :class:`Stroke`\n"
" - :class:`ViewEdge`\n"
"\n"
"- :class:`Iterator`\n"
"\n"
" - :class:`AdjacencyIterator`\n"
" - :class:`CurvePointIterator`\n"
" - :class:`Interface0DIterator`\n"
" - :class:`SVertexIterator`\n"
" - :class:`StrokeVertexIterator`\n"
" - :class:`ViewEdgeIterator`\n"
"\n"
" - :class:`ChainingIterator`\n"
"\n"
" - :class:`ChainPredicateIterator`\n"
" - :class:`ChainSilhouetteIterator`\n"
"\n"
" - :class:`orientedViewEdgeIterator`\n"
"\n"
"- :class:`Material`\n"
"- :class:`Noise`\n"
"- :class:`Operators`\n"
"- :class:`SShape`\n"
"- :class:`StrokeAttribute`\n"
"- :class:`StrokeShader`\n"
"\n"
" - :class:`BackboneStretcherShader`\n"
" - :class:`BezierCurveShader`\n"
" - :class:`BlenderTextureShader`\n"
" - :class:`CalligraphicShader`\n"
" - :class:`ColorNoiseShader`\n"
" - :class:`ColorVariationPatternShader`\n"
" - :class:`ConstantColorShader`\n"
" - :class:`ConstantThicknessShader`\n"
" - :class:`ConstrainedIncreasingThicknessShader`\n"
" - :class:`GuidingLinesShader`\n"
" - :class:`IncreasingColorShader`\n"
" - :class:`IncreasingThicknessShader`\n"
" - :class:`PolygonalizationShader`\n"
" - :class:`SamplingShader`\n"
" - :class:`SmoothingShader`\n"
" - :class:`SpatialNoiseShader`\n"
" - :class:`StrokeTextureShader`\n"
" - :class:`StrokeTextureStepShader`\n"
" - :class:`TextureAssignerShader`\n"
" - :class:`ThicknessNoiseShader`\n"
" - :class:`ThicknessVariationPatternShader`\n"
" - :class:`TipRemoverShader`\n"
" - :class:`fstreamShader`\n"
" - :class:`streamShader`\n"
"\n"
"- :class:`UnaryFunction0D`\n"
"\n"
" - :class:`UnaryFunction0DDouble`\n"
"\n"
" - :class:`Curvature2DAngleF0D`\n"
" - :class:`DensityF0D`\n"
" - :class:`GetProjectedXF0D`\n"
" - :class:`GetProjectedYF0D`\n"
" - :class:`GetProjectedZF0D`\n"
" - :class:`GetXF0D`\n"
" - :class:`GetYF0D`\n"
" - :class:`GetZF0D`\n"
" - :class:`LocalAverageDepthF0D`\n"
" - :class:`ZDiscontinuityF0D`\n"
"\n"
" - :class:`UnaryFunction0DEdgeNature`\n"
"\n"
" - :class:`CurveNatureF0D`\n"
"\n"
" - :class:`UnaryFunction0DFloat`\n"
"\n"
" - :class:`GetCurvilinearAbscissaF0D`\n"
" - :class:`GetParameterF0D`\n"
" - :class:`GetViewMapGradientNormF0D`\n"
" - :class:`ReadCompleteViewMapPixelF0D`\n"
" - :class:`ReadMapPixelF0D`\n"
" - :class:`ReadSteerableViewMapPixelF0D`\n"
"\n"
" - :class:`UnaryFunction0DId`\n"
"\n"
" - :class:`ShapeIdF0D`\n"
"\n"
" - :class:`UnaryFunction0DMaterial`\n"
"\n"
" - :class:`MaterialF0D`\n"
"\n"
" - :class:`UnaryFunction0DUnsigned`\n"
"\n"
" - :class:`QuantitativeInvisibilityF0D`\n"
"\n"
" - :class:`UnaryFunction0DVec2f`\n"
"\n"
" - :class:`Normal2DF0D`\n"
" - :class:`VertexOrientation2DF0D`\n"
"\n"
" - :class:`UnaryFunction0DVec3f`\n"
"\n"
" - :class:`VertexOrientation3DF0D`\n"
"\n"
" - :class:`UnaryFunction0DVectorViewShape`\n"
"\n"
" - :class:`GetOccludersF0D`\n"
"\n"
" - :class:`UnaryFunction0DViewShape`\n"
"\n"
" - :class:`GetOccludeeF0D`\n"
" - :class:`GetShapeF0D`\n"
"\n"
"- :class:`UnaryFunction1D`\n"
"\n"
" - :class:`UnaryFunction1DDouble`\n"
"\n"
" - :class:`Curvature2DAngleF1D`\n"
" - :class:`DensityF1D`\n"
" - :class:`GetCompleteViewMapDensityF1D`\n"
" - :class:`GetDirectionalViewMapDensityF1D`\n"
" - :class:`GetProjectedXF1D`\n"
" - :class:`GetProjectedYF1D`\n"
" - :class:`GetProjectedZF1D`\n"
" - :class:`GetSteerableViewMapDensityF1D`\n"
" - :class:`GetViewMapGradientNormF1D`\n"
" - :class:`GetXF1D`\n"
" - :class:`GetYF1D`\n"
" - :class:`GetZF1D`\n"
" - :class:`LocalAverageDepthF1D`\n"
" - :class:`ZDiscontinuityF1D`\n"
"\n"
" - :class:`UnaryFunction1DEdgeNature`\n"
"\n"
" - :class:`CurveNatureF1D`\n"
"\n"
" - :class:`UnaryFunction1DFloat`\n"
" - :class:`UnaryFunction1DUnsigned`\n"
"\n"
" - :class:`QuantitativeInvisibilityF1D`\n"
"\n"
" - :class:`UnaryFunction1DVec2f`\n"
"\n"
" - :class:`Normal2DF1D`\n"
" - :class:`Orientation2DF1D`\n"
"\n"
" - :class:`UnaryFunction1DVec3f`\n"
"\n"
" - :class:`Orientation3DF1D`\n"
"\n"
" - :class:`UnaryFunction1DVectorViewShape`\n"
"\n"
" - :class:`GetOccludeeF1D`\n"
" - :class:`GetOccludersF1D`\n"
" - :class:`GetShapeF1D`\n"
"\n"
" - :class:`UnaryFunction1DVoid`\n"
"\n"
" - :class:`ChainingTimeStampF1D`\n"
" - :class:`IncrementChainingTimeStampF1D`\n"
" - :class:`TimeStampF1D`\n"
"\n"
"- :class:`UnaryPredicate0D`\n"
"\n"
" - :class:`FalseUP0D`\n"
" - :class:`TrueUP0D`\n"
"\n"
"- :class:`UnaryPredicate1D`\n"
"\n"
" - :class:`ContourUP1D`\n"
" - :class:`DensityLowerThanUP1D`\n"
" - :class:`EqualToChainingTimeStampUP1D`\n"
" - :class:`EqualToTimeStampUP1D`\n"
" - :class:`ExternalContourUP1D`\n"
" - :class:`FalseUP1D`\n"
" - :class:`QuantitativeInvisibilityUP1D`\n"
" - :class:`ShapeUP1D`\n"
" - :class:`TrueUP1D`\n"
" - :class:`WithinImageBoundaryUP1D`\n"
"\n"
"- :class:`ViewMap`\n"
"- :class:`ViewShape`\n"
"- :class:`IntegrationType`\n"
"- :class:`MediumType`\n"
"- :class:`Nature`\n"
"\n");
/*-----------------------Freestyle module method def---------------------------*/
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wcast-function-type"
# else
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
# endif
#endif
static PyMethodDef module_functions[] = {
{"getCurrentScene",
(PyCFunction)Freestyle_getCurrentScene,
METH_NOARGS,
Freestyle_getCurrentScene___doc__},
{"blendRamp", (PyCFunction)Freestyle_blendRamp, METH_VARARGS, Freestyle_blendRamp___doc__},
{"evaluateColorRamp",
(PyCFunction)Freestyle_evaluateColorRamp,
METH_VARARGS,
Freestyle_evaluateColorRamp___doc__},
{"evaluateCurveMappingF",
(PyCFunction)Freestyle_evaluateCurveMappingF,
METH_VARARGS,
Freestyle_evaluateCurveMappingF___doc__},
{nullptr, nullptr, 0, nullptr},
};
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic pop
# else
# pragma GCC diagnostic pop
# endif
#endif
/*-----------------------Freestyle module definition---------------------------*/
static PyModuleDef module_definition = {
/*m_base*/ PyModuleDef_HEAD_INIT,
/*m_name*/ "_freestyle",
/*m_doc*/ module_docstring,
/*m_size*/ -1,
/*m_methods*/ module_functions,
/*m_slots*/ nullptr,
/*m_traverse*/ nullptr,
/*m_clear*/ nullptr,
/*m_free*/ nullptr,
};
//-------------------MODULE INITIALIZATION--------------------------------
PyObject *Freestyle_Init()
{
PyObject *module;
// initialize modules
module = PyModule_Create(&module_definition);
if (!module) {
return nullptr;
}
PyDict_SetItemString(PySys_GetObject("modules"), module_definition.m_name, module);
// update 'sys.path' for Freestyle Python API modules
const std::optional<std::string> path = BKE_appdir_folder_id(blender::BLENDER_SYSTEM_SCRIPTS,
"freestyle");
if (path.has_value()) {
char modpath[FILE_MAX];
blender::BLI_path_join(modpath, sizeof(modpath), path->c_str(), "modules");
PyObject *sys_path = PySys_GetObject("path"); /* borrow */
PyObject *py_modpath = blender::PyC_UnicodeFromBytes(modpath);
PyList_Append(sys_path, py_modpath);
Py_DECREF(py_modpath);
#if 0
printf("Adding Python path: %s\n", modpath);
#endif
}
else {
printf(
"Freestyle: couldn't find 'scripts/freestyle/modules', Freestyle won't work properly.\n");
}
// attach its classes (adding the object types to the module)
// those classes have to be initialized before the others
MediumType_Init(module);
Nature_Init(module);
BBox_Init(module);
BinaryPredicate0D_Init(module);
BinaryPredicate1D_Init(module);
ContextFunctions_Init(module);
FrsMaterial_Init(module);
FrsNoise_Init(module);
Id_Init(module);
IntegrationType_Init(module);
Interface0D_Init(module);
Interface1D_Init(module);
Iterator_Init(module);
Operators_Init(module);
SShape_Init(module);
StrokeAttribute_Init(module);
StrokeShader_Init(module);
UnaryFunction0D_Init(module);
UnaryFunction1D_Init(module);
UnaryPredicate0D_Init(module);
UnaryPredicate1D_Init(module);
ViewMap_Init(module);
ViewShape_Init(module);
return module;
}
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,19 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
#include <Python.h>
///////////////////////////////////////////////////////////////////////////////////////////
/*---------------------------Python BPy_Freestyle visible prototypes-----------*/
PyObject *Freestyle_Init(void);
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,594 @@
/* SPDX-FileCopyrightText: 2004-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_FrsMaterial.h"
#include "BPy_Convert.h"
#include "BLI_hash_mm2a.hh"
#include "BLI_math_vector.h"
using namespace Freestyle;
///////////////////////////////////////////////////////////////////////////////////////////
//-------------------MODULE INITIALIZATION--------------------------------
int FrsMaterial_Init(PyObject *module)
{
if (module == nullptr) {
return -1;
}
if (PyType_Ready(&FrsMaterial_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "Material", (PyObject *)&FrsMaterial_Type);
FrsMaterial_mathutils_register_callback();
return 0;
}
//------------------------INSTANCE METHODS ----------------------------------
PyDoc_STRVAR(
/* Wrap. */
FrsMaterial_doc,
"Class defining a material.\n"
"\n"
".. method:: __init__(*args)\n"
"\n"
" Accepted call signatures:\n"
"\n"
" - ``__init__()``\n"
" - ``__init__(brother)``\n"
" - ``__init__(line, diffuse, ambient, specular, emission, shininess, priority)``\n"
"\n"
" Creates a :class:`FrsMaterial` using either default constructor,\n"
" copy constructor, or an overloaded constructor\n"
"\n"
" :param brother: A Material object to be used as a copy constructor.\n"
" :type brother: :class:`Material`\n"
" :param line: The line color.\n"
" :type line: :class:`mathutils.Vector` | tuple[float, float, float, float] | list[float]\n"
" :param diffuse: The diffuse color.\n"
" :type diffuse: \n"
" :param ambient: The ambient color.\n"
" :type ambient: :class:`mathutils.Vector` | tuple[float, float, float, float] | "
"list[float]\n"
" :param specular: The specular color.\n"
" :type specular: :class:`mathutils.Vector` | tuple[float, float, float, float] | "
"list[float]\n"
" :param emission: The emissive color.\n"
" :type emission: :class:`mathutils.Vector` | tuple[float, float, float, float] | "
"list[float]\n"
" :param shininess: The shininess coefficient.\n"
" :type shininess: float\n"
" :param priority: The line color priority.\n"
" :type priority: int\n");
static int FrsMaterial_init(BPy_FrsMaterial *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist_1[] = {"brother", nullptr};
static const char *kwlist_2[] = {
"line", "diffuse", "ambient", "specular", "emission", "shininess", "priority", nullptr};
PyObject *brother = nullptr;
float line[4], diffuse[4], ambient[4], specular[4], emission[4], shininess;
int priority;
if (PyArg_ParseTupleAndKeywords(
args, kwds, "|O!", (char **)kwlist_1, &FrsMaterial_Type, &brother))
{
if (!brother) {
self->m = new FrsMaterial();
}
else {
FrsMaterial *m = ((BPy_FrsMaterial *)brother)->m;
if (!m) {
PyErr_SetString(PyExc_RuntimeError, "invalid Material object");
return -1;
}
self->m = new FrsMaterial(*m);
}
}
else if ((void)PyErr_Clear(),
PyArg_ParseTupleAndKeywords(args,
kwds,
"O&O&O&O&O&fi",
(char **)kwlist_2,
convert_v4,
line,
convert_v4,
diffuse,
convert_v4,
ambient,
convert_v4,
specular,
convert_v4,
emission,
&shininess,
&priority))
{
self->m = new FrsMaterial(line, diffuse, ambient, specular, emission, shininess, priority);
}
else {
PyErr_SetString(PyExc_TypeError, "invalid argument(s)");
return -1;
}
return 0;
}
static void FrsMaterial_dealloc(BPy_FrsMaterial *self)
{
delete self->m;
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *FrsMaterial_repr(BPy_FrsMaterial *self)
{
return PyUnicode_FromFormat("Material - address: %p", self->m);
}
/*----------------------mathutils callbacks ----------------------------*/
/* subtype */
#define MATHUTILS_SUBTYPE_DIFFUSE 1
#define MATHUTILS_SUBTYPE_SPECULAR 2
#define MATHUTILS_SUBTYPE_AMBIENT 3
#define MATHUTILS_SUBTYPE_EMISSION 4
#define MATHUTILS_SUBTYPE_LINE 5
static int FrsMaterial_mathutils_check(blender::BaseMathObject *bmo)
{
if (!BPy_FrsMaterial_Check(bmo->cb_user)) {
return -1;
}
return 0;
}
static int FrsMaterial_mathutils_get(blender::BaseMathObject *bmo, int subtype)
{
BPy_FrsMaterial *self = (BPy_FrsMaterial *)bmo->cb_user;
switch (subtype) {
case MATHUTILS_SUBTYPE_LINE:
bmo->data[0] = self->m->lineR();
bmo->data[1] = self->m->lineG();
bmo->data[2] = self->m->lineB();
bmo->data[3] = self->m->lineA();
break;
case MATHUTILS_SUBTYPE_DIFFUSE:
bmo->data[0] = self->m->diffuseR();
bmo->data[1] = self->m->diffuseG();
bmo->data[2] = self->m->diffuseB();
bmo->data[3] = self->m->diffuseA();
break;
case MATHUTILS_SUBTYPE_SPECULAR:
bmo->data[0] = self->m->specularR();
bmo->data[1] = self->m->specularG();
bmo->data[2] = self->m->specularB();
bmo->data[3] = self->m->specularA();
break;
case MATHUTILS_SUBTYPE_AMBIENT:
bmo->data[0] = self->m->ambientR();
bmo->data[1] = self->m->ambientG();
bmo->data[2] = self->m->ambientB();
bmo->data[3] = self->m->ambientA();
break;
case MATHUTILS_SUBTYPE_EMISSION:
bmo->data[0] = self->m->emissionR();
bmo->data[1] = self->m->emissionG();
bmo->data[2] = self->m->emissionB();
bmo->data[3] = self->m->emissionA();
break;
default:
return -1;
}
return 0;
}
static int FrsMaterial_mathutils_set(blender::BaseMathObject *bmo, int subtype)
{
BPy_FrsMaterial *self = (BPy_FrsMaterial *)bmo->cb_user;
switch (subtype) {
case MATHUTILS_SUBTYPE_LINE:
self->m->setLine(bmo->data[0], bmo->data[1], bmo->data[2], bmo->data[3]);
break;
case MATHUTILS_SUBTYPE_DIFFUSE:
self->m->setDiffuse(bmo->data[0], bmo->data[1], bmo->data[2], bmo->data[3]);
break;
case MATHUTILS_SUBTYPE_SPECULAR:
self->m->setSpecular(bmo->data[0], bmo->data[1], bmo->data[2], bmo->data[3]);
break;
case MATHUTILS_SUBTYPE_AMBIENT:
self->m->setAmbient(bmo->data[0], bmo->data[1], bmo->data[2], bmo->data[3]);
break;
case MATHUTILS_SUBTYPE_EMISSION:
self->m->setEmission(bmo->data[0], bmo->data[1], bmo->data[2], bmo->data[3]);
break;
default:
return -1;
}
return 0;
}
static int FrsMaterial_mathutils_get_index(blender::BaseMathObject *bmo, int subtype, int index)
{
BPy_FrsMaterial *self = (BPy_FrsMaterial *)bmo->cb_user;
switch (subtype) {
case MATHUTILS_SUBTYPE_LINE: {
const float *color = self->m->line();
bmo->data[index] = color[index];
break;
}
case MATHUTILS_SUBTYPE_DIFFUSE: {
const float *color = self->m->diffuse();
bmo->data[index] = color[index];
break;
}
case MATHUTILS_SUBTYPE_SPECULAR: {
const float *color = self->m->specular();
bmo->data[index] = color[index];
break;
}
case MATHUTILS_SUBTYPE_AMBIENT: {
const float *color = self->m->ambient();
bmo->data[index] = color[index];
break;
}
case MATHUTILS_SUBTYPE_EMISSION: {
const float *color = self->m->emission();
bmo->data[index] = color[index];
break;
}
default:
return -1;
}
return 0;
}
static int FrsMaterial_mathutils_set_index(blender::BaseMathObject *bmo, int subtype, int index)
{
BPy_FrsMaterial *self = (BPy_FrsMaterial *)bmo->cb_user;
float color[4];
switch (subtype) {
case MATHUTILS_SUBTYPE_LINE:
blender::copy_v4_v4(color, self->m->line());
color[index] = bmo->data[index];
self->m->setLine(color[0], color[1], color[2], color[3]);
break;
case MATHUTILS_SUBTYPE_DIFFUSE:
blender::copy_v4_v4(color, self->m->diffuse());
color[index] = bmo->data[index];
self->m->setDiffuse(color[0], color[1], color[2], color[3]);
break;
case MATHUTILS_SUBTYPE_SPECULAR:
blender::copy_v4_v4(color, self->m->specular());
color[index] = bmo->data[index];
self->m->setSpecular(color[0], color[1], color[2], color[3]);
break;
case MATHUTILS_SUBTYPE_AMBIENT:
blender::copy_v4_v4(color, self->m->ambient());
color[index] = bmo->data[index];
self->m->setAmbient(color[0], color[1], color[2], color[3]);
break;
case MATHUTILS_SUBTYPE_EMISSION:
blender::copy_v4_v4(color, self->m->emission());
color[index] = bmo->data[index];
self->m->setEmission(color[0], color[1], color[2], color[3]);
break;
default:
return -1;
}
return 0;
}
static blender::Mathutils_Callback FrsMaterial_mathutils_cb = {
FrsMaterial_mathutils_check,
FrsMaterial_mathutils_get,
FrsMaterial_mathutils_set,
FrsMaterial_mathutils_get_index,
FrsMaterial_mathutils_set_index,
};
static uchar FrsMaterial_mathutils_cb_index = -1;
void FrsMaterial_mathutils_register_callback()
{
FrsMaterial_mathutils_cb_index = Mathutils_RegisterCallback(&FrsMaterial_mathutils_cb);
}
/*----------------------FrsMaterial get/setters ----------------------------*/
PyDoc_STRVAR(
/* Wrap. */
FrsMaterial_line_doc,
"RGBA components of the line color of the material.\n"
"\n"
":type: :class:`mathutils.Vector`\n");
static PyObject *FrsMaterial_line_get(BPy_FrsMaterial *self, void * /*closure*/)
{
return blender::Vector_CreatePyObject_cb(
(PyObject *)self, 4, FrsMaterial_mathutils_cb_index, MATHUTILS_SUBTYPE_LINE);
}
static int FrsMaterial_line_set(BPy_FrsMaterial *self, PyObject *value, void * /*closure*/)
{
float color[4];
if (blender::mathutils_array_parse(color, 4, 4, value, "value must be a 4-dimensional vector") ==
-1)
{
return -1;
}
self->m->setLine(color[0], color[1], color[2], color[3]);
return 0;
}
PyDoc_STRVAR(
/* Wrap. */
FrsMaterial_diffuse_doc,
"RGBA components of the diffuse color of the material.\n"
"\n"
":type: :class:`mathutils.Vector`\n");
static PyObject *FrsMaterial_diffuse_get(BPy_FrsMaterial *self, void * /*closure*/)
{
return blender::Vector_CreatePyObject_cb(
(PyObject *)self, 4, FrsMaterial_mathutils_cb_index, MATHUTILS_SUBTYPE_DIFFUSE);
}
static int FrsMaterial_diffuse_set(BPy_FrsMaterial *self, PyObject *value, void * /*closure*/)
{
float color[4];
if (blender::mathutils_array_parse(color, 4, 4, value, "value must be a 4-dimensional vector") ==
-1)
{
return -1;
}
self->m->setDiffuse(color[0], color[1], color[2], color[3]);
return 0;
}
PyDoc_STRVAR(
/* Wrap. */
FrsMaterial_specular_doc,
"RGBA components of the specular color of the material.\n"
"\n"
":type: :class:`mathutils.Vector`\n");
static PyObject *FrsMaterial_specular_get(BPy_FrsMaterial *self, void * /*closure*/)
{
return blender::Vector_CreatePyObject_cb(
(PyObject *)self, 4, FrsMaterial_mathutils_cb_index, MATHUTILS_SUBTYPE_SPECULAR);
}
static int FrsMaterial_specular_set(BPy_FrsMaterial *self, PyObject *value, void * /*closure*/)
{
float color[4];
if (blender::mathutils_array_parse(color, 4, 4, value, "value must be a 4-dimensional vector") ==
-1)
{
return -1;
}
self->m->setSpecular(color[0], color[1], color[2], color[3]);
return 0;
}
PyDoc_STRVAR(
/* Wrap. */
FrsMaterial_ambient_doc,
"RGBA components of the ambient color of the material.\n"
"\n"
":type: :class:`mathutils.Color`\n");
static PyObject *FrsMaterial_ambient_get(BPy_FrsMaterial *self, void * /*closure*/)
{
return blender::Vector_CreatePyObject_cb(
(PyObject *)self, 4, FrsMaterial_mathutils_cb_index, MATHUTILS_SUBTYPE_AMBIENT);
}
static int FrsMaterial_ambient_set(BPy_FrsMaterial *self, PyObject *value, void * /*closure*/)
{
float color[4];
if (blender::mathutils_array_parse(color, 4, 4, value, "value must be a 4-dimensional vector") ==
-1)
{
return -1;
}
self->m->setAmbient(color[0], color[1], color[2], color[3]);
return 0;
}
PyDoc_STRVAR(
/* Wrap. */
FrsMaterial_emission_doc,
"RGBA components of the emissive color of the material.\n"
"\n"
":type: :class:`mathutils.Color`\n");
static PyObject *FrsMaterial_emission_get(BPy_FrsMaterial *self, void * /*closure*/)
{
return blender::Vector_CreatePyObject_cb(
(PyObject *)self, 4, FrsMaterial_mathutils_cb_index, MATHUTILS_SUBTYPE_EMISSION);
}
static int FrsMaterial_emission_set(BPy_FrsMaterial *self, PyObject *value, void * /*closure*/)
{
float color[4];
if (blender::mathutils_array_parse(color, 4, 4, value, "value must be a 4-dimensional vector") ==
-1)
{
return -1;
}
self->m->setEmission(color[0], color[1], color[2], color[3]);
return 0;
}
PyDoc_STRVAR(
/* Wrap. */
FrsMaterial_shininess_doc,
"Shininess coefficient of the material.\n"
"\n"
":type: float\n");
static PyObject *FrsMaterial_shininess_get(BPy_FrsMaterial *self, void * /*closure*/)
{
return PyFloat_FromDouble(self->m->shininess());
}
static int FrsMaterial_shininess_set(BPy_FrsMaterial *self, PyObject *value, void * /*closure*/)
{
float scalar;
if ((scalar = PyFloat_AsDouble(value)) == -1.0f && PyErr_Occurred()) {
/* parsed item not a number */
PyErr_SetString(PyExc_TypeError, "value must be a number");
return -1;
}
self->m->setShininess(scalar);
return 0;
}
PyDoc_STRVAR(
/* Wrap. */
FrsMaterial_priority_doc,
"Line color priority of the material.\n"
"\n"
":type: int\n");
static PyObject *FrsMaterial_priority_get(BPy_FrsMaterial *self, void * /*closure*/)
{
return PyLong_FromLong(self->m->priority());
}
static int FrsMaterial_priority_set(BPy_FrsMaterial *self, PyObject *value, void * /*closure*/)
{
int scalar;
if ((scalar = PyLong_AsLong(value)) == -1 && PyErr_Occurred()) {
PyErr_SetString(PyExc_TypeError, "value must be an integer");
return -1;
}
self->m->setPriority(scalar);
return 0;
}
static PyGetSetDef BPy_FrsMaterial_getseters[] = {
{"line",
(getter)FrsMaterial_line_get,
(setter)FrsMaterial_line_set,
FrsMaterial_line_doc,
nullptr},
{"diffuse",
(getter)FrsMaterial_diffuse_get,
(setter)FrsMaterial_diffuse_set,
FrsMaterial_diffuse_doc,
nullptr},
{"specular",
(getter)FrsMaterial_specular_get,
(setter)FrsMaterial_specular_set,
FrsMaterial_specular_doc,
nullptr},
{"ambient",
(getter)FrsMaterial_ambient_get,
(setter)FrsMaterial_ambient_set,
FrsMaterial_ambient_doc,
nullptr},
{"emission",
(getter)FrsMaterial_emission_get,
(setter)FrsMaterial_emission_set,
FrsMaterial_emission_doc,
nullptr},
{"shininess",
(getter)FrsMaterial_shininess_get,
(setter)FrsMaterial_shininess_set,
FrsMaterial_shininess_doc,
nullptr},
{"priority",
(getter)FrsMaterial_priority_get,
(setter)FrsMaterial_priority_set,
FrsMaterial_priority_doc,
nullptr},
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
};
static PyObject *BPy_FrsMaterial_richcmpr(PyObject *objectA,
PyObject *objectB,
int comparison_type)
{
const BPy_FrsMaterial *matA = nullptr, *matB = nullptr;
bool result = false;
if (!BPy_FrsMaterial_Check(objectA) || !BPy_FrsMaterial_Check(objectB)) {
if (comparison_type == Py_NE) {
Py_RETURN_TRUE;
}
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 nullptr;
}
if (result == true) {
Py_RETURN_TRUE;
}
Py_RETURN_FALSE;
}
static Py_hash_t FrsMaterial_hash(PyObject *self)
{
return (Py_uhash_t)blender::BLI_hash_mm2((const uchar *)self, sizeof(*self), 0);
}
/*-----------------------BPy_FrsMaterial type definition ------------------------------*/
PyTypeObject FrsMaterial_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "Material",
/*tp_basicsize*/ sizeof(BPy_FrsMaterial),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ (destructor)FrsMaterial_dealloc,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ (reprfunc)FrsMaterial_repr,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ (hashfunc)FrsMaterial_hash,
/*tp_call*/ nullptr,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
/*tp_doc*/ FrsMaterial_doc,
/*tp_traverse*/ nullptr,
/*tp_clear*/ nullptr,
/*tp_richcompare*/ (richcmpfunc)BPy_FrsMaterial_richcmpr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ nullptr,
/*tp_members*/ nullptr,
/*tp_getset*/ BPy_FrsMaterial_getseters,
/*tp_base*/ nullptr,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ (initproc)FrsMaterial_init,
/*tp_alloc*/ nullptr,
/*tp_new*/ PyType_GenericNew,
};
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,35 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
extern "C" {
#include <Python.h>
}
#include "../scene_graph/FrsMaterial.h"
///////////////////////////////////////////////////////////////////////////////////////////
extern PyTypeObject FrsMaterial_Type;
#define BPy_FrsMaterial_Check(v) \
(PyObject_IsInstance((PyObject *)v, (PyObject *)&FrsMaterial_Type))
/*---------------------------Python BPy_FrsMaterial structure definition----------*/
struct BPy_FrsMaterial {
PyObject_HEAD
Freestyle::FrsMaterial *m;
};
/*---------------------------Python BPy_FrsMaterial visible prototypes-----------*/
int FrsMaterial_Init(PyObject *module);
void FrsMaterial_mathutils_register_callback();
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,381 @@
/* SPDX-FileCopyrightText: 2004-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_FrsNoise.h"
#include "BPy_Convert.h"
#include "../system/RandGen.h"
#include "BLI_sys_types.h"
#include <sstream>
using namespace Freestyle;
///////////////////////////////////////////////////////////////////////////////////////////
//-------------------MODULE INITIALIZATION--------------------------------
int FrsNoise_Init(PyObject *module)
{
if (module == nullptr) {
return -1;
}
if (PyType_Ready(&FrsNoise_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "Noise", (PyObject *)&FrsNoise_Type);
return 0;
}
//------------------------INSTANCE METHODS ----------------------------------
PyDoc_STRVAR(
/* Wrap. */
FrsNoise_doc,
"Class to provide Perlin noise functionalities.\n"
"\n"
".. method:: __init__(seed = -1)\n"
"\n"
" Builds a Noise object. Seed is an optional argument. The seed value is used\n"
" as a seed for random number generation if it is equal to or greater than zero;\n"
" otherwise, time is used as a seed.\n"
"\n"
" :param seed: Seed for random number generation.\n"
" :type seed: int\n");
static int FrsNoise_init(BPy_FrsNoise *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"seed", nullptr};
long seed = -1;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|l", (char **)kwlist, &seed)) {
return -1;
}
self->n = new Noise(seed);
self->pn = new PseudoNoise();
return 0;
}
static void FrsNoise_dealloc(BPy_FrsNoise *self)
{
delete self->n;
delete self->pn;
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *FrsNoise_repr(BPy_FrsNoise *self)
{
return PyUnicode_FromFormat("Noise - address: %p", self->n);
}
PyDoc_STRVAR(
/* Wrap. */
FrsNoise_turbulence1_doc,
".. method:: turbulence1(v, freq, amp, oct=4)\n"
"\n"
" Returns a noise value for a 1D element.\n"
"\n"
" :param v: One-dimensional sample point.\n"
" :type v: float\n"
" :param freq: Noise frequency.\n"
" :type freq: float\n"
" :param amp: Amplitude.\n"
" :type amp: float\n"
" :param oct: Number of octaves.\n"
" :type oct: int\n"
" :return: A noise value.\n"
" :rtype: float\n");
static PyObject *FrsNoise_drand(BPy_FrsNoise * /*self*/, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"seed", nullptr};
long seed = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|I", (char **)kwlist, &seed)) {
PyErr_SetString(PyExc_TypeError, "optional argument 1 must be of type int");
return nullptr;
}
if (seed) {
RandGen::srand48(seed);
}
return PyFloat_FromDouble(RandGen::drand48());
}
static PyObject *FrsNoise_turbulence_smooth(BPy_FrsNoise *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"v", "oct", nullptr};
double x; // NOTE: this has to be a double (not float)
uint nbOctaves = 8;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "d|I", (char **)kwlist, &x, &nbOctaves)) {
return nullptr;
}
return PyFloat_FromDouble(self->pn->turbulenceSmooth(x, nbOctaves));
}
static PyObject *FrsNoise_turbulence1(BPy_FrsNoise *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"v", "freq", "amp", "oct", nullptr};
float f1, f2, f3;
uint i = 4;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "fff|I", (char **)kwlist, &f1, &f2, &f3, &i)) {
return nullptr;
}
return PyFloat_FromDouble(self->n->turbulence1(f1, f2, f3, i));
}
PyDoc_STRVAR(
/* Wrap. */
FrsNoise_turbulence2_doc,
".. method:: turbulence2(v, freq, amp, oct=4)\n"
"\n"
" Returns a noise value for a 2D element.\n"
"\n"
" :param v: Two-dimensional sample point.\n"
" :type v: :class:`mathutils.Vector` | tuple[float, float] | list[float]\n"
" :param freq: Noise frequency.\n"
" :type freq: float\n"
" :param amp: Amplitude.\n"
" :type amp: float\n"
" :param oct: Number of octaves.\n"
" :type oct: int\n"
" :return: A noise value.\n"
" :rtype: float\n");
static PyObject *FrsNoise_turbulence2(BPy_FrsNoise *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"v", "freq", "amp", "oct", nullptr};
PyObject *obj1;
float f2, f3;
uint i = 4;
Vec2f vec;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "Off|I", (char **)kwlist, &obj1, &f2, &f3, &i)) {
return nullptr;
}
if (!Vec2f_ptr_from_PyObject(obj1, vec)) {
PyErr_SetString(PyExc_TypeError,
"argument 1 must be a 2D vector (either a list of 2 elements or Vector)");
return nullptr;
}
float t = self->n->turbulence2(vec, f2, f3, i);
return PyFloat_FromDouble(t);
}
PyDoc_STRVAR(
/* Wrap. */
FrsNoise_turbulence3_doc,
".. method:: turbulence3(v, freq, amp, oct=4)\n"
"\n"
" Returns a noise value for a 3D element.\n"
"\n"
" :param v: Three-dimensional sample point.\n"
" :type v: :class:`mathutils.Vector` | tuple[float, float, float] | list[float]\n"
" :param freq: Noise frequency.\n"
" :type freq: float\n"
" :param amp: Amplitude.\n"
" :type amp: float\n"
" :param oct: Number of octaves.\n"
" :type oct: int\n"
" :return: A noise value.\n"
" :rtype: float\n");
static PyObject *FrsNoise_turbulence3(BPy_FrsNoise *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"v", "freq", "amp", "oct", nullptr};
PyObject *obj1;
float f2, f3;
uint i = 4;
Vec3f vec;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "Off|I", (char **)kwlist, &obj1, &f2, &f3, &i)) {
return nullptr;
}
if (!Vec3f_ptr_from_PyObject(obj1, vec)) {
PyErr_SetString(PyExc_TypeError,
"argument 1 must be a 3D vector (either a list of 3 elements or Vector)");
return nullptr;
}
float t = self->n->turbulence3(vec, f2, f3, i);
return PyFloat_FromDouble(t);
}
PyDoc_STRVAR(
/* Wrap. */
FrsNoise_smoothNoise1_doc,
".. method:: smoothNoise1(v)\n"
"\n"
" Returns a smooth noise value for a 1D element.\n"
"\n"
" :param v: One-dimensional sample point.\n"
" :type v: float\n"
" :return: A smooth noise value.\n"
" :rtype: float\n");
static PyObject *FrsNoise_smoothNoise1(BPy_FrsNoise *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"v", nullptr};
float f;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "f", (char **)kwlist, &f)) {
return nullptr;
}
return PyFloat_FromDouble(self->n->smoothNoise1(f));
}
PyDoc_STRVAR(
/* Wrap. */
FrsNoise_smoothNoise2_doc,
".. method:: smoothNoise2(v)\n"
"\n"
" Returns a smooth noise value for a 2D element.\n"
"\n"
" :param v: Two-dimensional sample point.\n"
" :type v: :class:`mathutils.Vector` | tuple[float, float] | list[float]\n"
" :return: A smooth noise value.\n"
" :rtype: float\n");
static PyObject *FrsNoise_smoothNoise2(BPy_FrsNoise *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"v", nullptr};
PyObject *obj;
Vec2f vec;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", (char **)kwlist, &obj)) {
return nullptr;
}
if (!Vec2f_ptr_from_PyObject(obj, vec)) {
PyErr_SetString(PyExc_TypeError,
"argument 1 must be a 2D vector (either a list of 2 elements or Vector)");
return nullptr;
}
float t = self->n->smoothNoise2(vec);
return PyFloat_FromDouble(t);
}
PyDoc_STRVAR(
/* Wrap. */
FrsNoise_smoothNoise3_doc,
".. method:: smoothNoise3(v)\n"
"\n"
" Returns a smooth noise value for a 3D element.\n"
"\n"
" :param v: Three-dimensional sample point.\n"
" :type v: :class:`mathutils.Vector` | tuple[float, float, float] | list[float]\n"
" :return: A smooth noise value.\n"
" :rtype: float\n");
static PyObject *FrsNoise_smoothNoise3(BPy_FrsNoise *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"v", nullptr};
PyObject *obj;
Vec3f vec;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", (char **)kwlist, &obj)) {
return nullptr;
}
if (!Vec3f_ptr_from_PyObject(obj, vec)) {
PyErr_SetString(PyExc_TypeError,
"argument 1 must be a 3D vector (either a list of 3 elements or Vector)");
return nullptr;
}
float t = self->n->smoothNoise3(vec);
return PyFloat_FromDouble(t);
}
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wcast-function-type"
# else
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
# endif
#endif
static PyMethodDef BPy_FrsNoise_methods[] = {
{"turbulence1",
(PyCFunction)FrsNoise_turbulence1,
METH_VARARGS | METH_KEYWORDS,
FrsNoise_turbulence1_doc},
{"turbulence2",
(PyCFunction)FrsNoise_turbulence2,
METH_VARARGS | METH_KEYWORDS,
FrsNoise_turbulence2_doc},
{"turbulence3",
(PyCFunction)FrsNoise_turbulence3,
METH_VARARGS | METH_KEYWORDS,
FrsNoise_turbulence3_doc},
{"smoothNoise1",
(PyCFunction)FrsNoise_smoothNoise1,
METH_VARARGS | METH_KEYWORDS,
FrsNoise_smoothNoise1_doc},
{"smoothNoise2",
(PyCFunction)FrsNoise_smoothNoise2,
METH_VARARGS | METH_KEYWORDS,
FrsNoise_smoothNoise2_doc},
{"smoothNoise3",
(PyCFunction)FrsNoise_smoothNoise3,
METH_VARARGS | METH_KEYWORDS,
FrsNoise_smoothNoise3_doc},
{"rand", (PyCFunction)FrsNoise_drand, METH_VARARGS | METH_KEYWORDS, nullptr},
{"turbulence_smooth",
(PyCFunction)FrsNoise_turbulence_smooth,
METH_VARARGS | METH_KEYWORDS,
nullptr},
{nullptr, nullptr, 0, nullptr},
};
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic pop
# else
# pragma GCC diagnostic pop
# endif
#endif
/*-----------------------BPy_FrsNoise type definition ------------------------------*/
PyTypeObject FrsNoise_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "Noise",
/*tp_basicsize*/ sizeof(BPy_FrsNoise),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ (destructor)FrsNoise_dealloc,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ (reprfunc)FrsNoise_repr,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ nullptr,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
/*tp_doc*/ FrsNoise_doc,
/*tp_traverse*/ nullptr,
/*tp_clear*/ nullptr,
/*tp_richcompare*/ nullptr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ BPy_FrsNoise_methods,
/*tp_members*/ nullptr,
/*tp_getset*/ nullptr,
/*tp_base*/ nullptr,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ (initproc)FrsNoise_init,
/*tp_alloc*/ nullptr,
/*tp_new*/ PyType_GenericNew,
};
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,35 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
extern "C" {
#include <Python.h>
}
#include "../geometry/Noise.h"
#include "../system/PseudoNoise.h"
///////////////////////////////////////////////////////////////////////////////////////////
extern PyTypeObject FrsNoise_Type;
#define BPy_FrsNoise_Check(v) (PyObject_IsInstance((PyObject *)v, (PyObject *)&FrsNoise_Type))
/*---------------------------Python BPy_FrsNoise structure definition----------*/
struct BPy_FrsNoise {
PyObject_HEAD
Freestyle::Noise *n;
Freestyle::PseudoNoise *pn;
};
/*---------------------------Python BPy_FrsNoise visible prototypes-----------*/
int FrsNoise_Init(PyObject *module);
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,202 @@
/* SPDX-FileCopyrightText: 2004-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_Id.h"
#include "BPy_Convert.h"
using namespace Freestyle;
///////////////////////////////////////////////////////////////////////////////////////////
//-------------------MODULE INITIALIZATION--------------------------------
int Id_Init(PyObject *module)
{
if (module == nullptr) {
return -1;
}
if (PyType_Ready(&Id_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "Id", (PyObject *)&Id_Type);
return 0;
}
//------------------------INSTANCE METHODS ----------------------------------
PyDoc_STRVAR(
/* Wrap. */
Id_doc,
"Class for representing an object Id.\n"
"\n"
".. method:: __init__(*args, **kwargs)\n"
"\n"
" Accepted call signatures:\n"
"\n"
" - ``__init__(brother)``\n"
" - ``__init__(first=0, second=0)``\n"
"\n"
" Build the Id from two numbers or another :class:`Id` using the copy constructor.\n"
"\n"
" :param brother: An Id object.\n"
" :type brother: :class:`Id`\n"
" :param first: The first number.\n"
" :type first: int\n"
" :param second: The second number.\n"
" :type second: int\n");
static int Id_init(BPy_Id *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist_1[] = {"brother", nullptr};
static const char *kwlist_2[] = {"first", "second", nullptr};
PyObject *brother;
int first = 0, second = 0;
if (PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist_1, &Id_Type, &brother)) {
self->id = new Id(*(((BPy_Id *)brother)->id));
}
else if ((void)PyErr_Clear(),
PyArg_ParseTupleAndKeywords(args, kwds, "|ii", (char **)kwlist_2, &first, &second))
{
self->id = new Id(first, second);
}
else {
PyErr_SetString(PyExc_TypeError, "invalid argument(s)");
return -1;
}
return 0;
}
static void Id_dealloc(BPy_Id *self)
{
delete self->id;
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *Id_repr(BPy_Id *self)
{
return PyUnicode_FromFormat(
"[ first: %i, second: %i ](BPy_Id)", self->id->getFirst(), self->id->getSecond());
}
static PyObject *Id_RichCompare(BPy_Id *o1, BPy_Id *o2, int opid)
{
switch (opid) {
case Py_LT:
return PyBool_from_bool(o1->id->operator<(*(o2->id)));
case Py_LE:
return PyBool_from_bool(o1->id->operator<(*(o2->id)) || o1->id->operator==(*(o2->id)));
case Py_EQ:
return PyBool_from_bool(o1->id->operator==(*(o2->id)));
case Py_NE:
return PyBool_from_bool(o1->id->operator!=(*(o2->id)));
case Py_GT:
return PyBool_from_bool(!(o1->id->operator<(*(o2->id)) || o1->id->operator==(*(o2->id))));
case Py_GE:
return PyBool_from_bool(!o1->id->operator<(*(o2->id)));
}
Py_RETURN_NONE;
}
/*----------------------Id get/setters ----------------------------*/
PyDoc_STRVAR(
/* Wrap. */
Id_first_doc,
"The first number constituting the Id.\n"
"\n"
":type: int\n");
static PyObject *Id_first_get(BPy_Id *self, void * /*closure*/)
{
return PyLong_FromLong(self->id->getFirst());
}
static int Id_first_set(BPy_Id *self, PyObject *value, void * /*closure*/)
{
int scalar;
if ((scalar = PyLong_AsLong(value)) == -1 && PyErr_Occurred()) {
PyErr_SetString(PyExc_TypeError, "value must be an integer");
return -1;
}
self->id->setFirst(scalar);
return 0;
}
PyDoc_STRVAR(
/* Wrap. */
Id_second_doc,
"The second number constituting the Id.\n"
"\n"
":type: int\n");
static PyObject *Id_second_get(BPy_Id *self, void * /*closure*/)
{
return PyLong_FromLong(self->id->getSecond());
}
static int Id_second_set(BPy_Id *self, PyObject *value, void * /*closure*/)
{
int scalar;
if ((scalar = PyLong_AsLong(value)) == -1 && PyErr_Occurred()) {
PyErr_SetString(PyExc_TypeError, "value must be an integer");
return -1;
}
self->id->setSecond(scalar);
return 0;
}
static PyGetSetDef BPy_Id_getseters[] = {
{"first", (getter)Id_first_get, (setter)Id_first_set, Id_first_doc, nullptr},
{"second", (getter)Id_second_get, (setter)Id_second_set, Id_second_doc, nullptr},
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
};
/*-----------------------BPy_Id type definition ------------------------------*/
PyTypeObject Id_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "Id",
/*tp_basicsize*/ sizeof(BPy_Id),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ (destructor)Id_dealloc,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ (reprfunc)Id_repr,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ nullptr,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
/*tp_doc*/ Id_doc,
/*tp_traverse*/ nullptr,
/*tp_clear*/ nullptr,
/*tp_richcompare*/ (richcmpfunc)Id_RichCompare,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ nullptr,
/*tp_members*/ nullptr,
/*tp_getset*/ BPy_Id_getseters,
/*tp_base*/ nullptr,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ (initproc)Id_init,
/*tp_alloc*/ nullptr,
/*tp_new*/ PyType_GenericNew,
};
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,35 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
extern "C" {
#include <Python.h>
}
#include <iostream>
#include "../system/Id.h"
///////////////////////////////////////////////////////////////////////////////////////////
extern PyTypeObject Id_Type;
#define BPy_Id_Check(v) (PyObject_IsInstance((PyObject *)v, (PyObject *)&Id_Type))
/*---------------------------Python BPy_Id structure definition----------*/
struct BPy_Id {
PyObject_HEAD
Freestyle::Id *id;
};
/*---------------------------Python BPy_Id visible prototypes-----------*/
int Id_Init(PyObject *module);
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,267 @@
/* SPDX-FileCopyrightText: 2004-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_IntegrationType.h"
#include "BPy_Convert.h"
#include "Iterator/BPy_Interface0DIterator.h"
#include "UnaryFunction0D/BPy_UnaryFunction0DDouble.h"
#include "UnaryFunction0D/BPy_UnaryFunction0DFloat.h"
#include "UnaryFunction0D/BPy_UnaryFunction0DUnsigned.h"
#include "BLI_sys_types.h"
using namespace Freestyle;
///////////////////////////////////////////////////////////////////////////////////////////
//------------------------ MODULE FUNCTIONS ----------------------------------
PyDoc_STRVAR(
/* Wrap. */
Integrator_integrate_doc,
".. function:: integrate(func, it, it_end, integration_type)\n"
"\n"
" Returns a single value from a set of values evaluated at each 0D\n"
" element of this 1D element.\n"
"\n"
" :param func: The UnaryFunction0D used to compute a value at each\n"
" Interface0D.\n"
" :type func: :class:`UnaryFunction0D`\n"
" :param it: The Interface0DIterator used to iterate over the 0D\n"
" elements of this 1D element. The integration will occur over\n"
" the 0D elements starting from the one pointed by it.\n"
" :type it: :class:`Interface0DIterator`\n"
" :param it_end: The Interface0DIterator pointing the end of the 0D\n"
" elements of the 1D element.\n"
" :type it_end: :class:`Interface0DIterator`\n"
" :param integration_type: The integration method used to compute a\n"
" single value from a set of values.\n"
" :type integration_type: :class:`IntegrationType`\n"
" :return: The single value obtained for the 1D element. The return\n"
" value type is float if func is of the :class:`UnaryFunction0DDouble`\n"
" or :class:`UnaryFunction0DFloat` type, and int if func is of the\n"
" :class:`UnaryFunction0DUnsigned` type.\n"
" :rtype: int | float\n");
static PyObject *Integrator_integrate(PyObject * /*self*/, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"func", "it", "it_end", "integration_type", nullptr};
PyObject *obj1, *obj4 = nullptr;
BPy_Interface0DIterator *obj2, *obj3;
if (!PyArg_ParseTupleAndKeywords(args,
kwds,
"O!O!O!|O!",
(char **)kwlist,
&UnaryFunction0D_Type,
&obj1,
&Interface0DIterator_Type,
&obj2,
&Interface0DIterator_Type,
&obj3,
&IntegrationType_Type,
&obj4))
{
return nullptr;
}
Interface0DIterator it(*(obj2->if0D_it)), it_end(*(obj3->if0D_it));
IntegrationType t = (obj4) ? IntegrationType_from_BPy_IntegrationType(obj4) : MEAN;
if (BPy_UnaryFunction0DDouble_Check(obj1)) {
UnaryFunction0D<double> *fun = ((BPy_UnaryFunction0DDouble *)obj1)->uf0D_double;
double res = integrate(*fun, it, it_end, t);
return PyFloat_FromDouble(res);
}
if (BPy_UnaryFunction0DFloat_Check(obj1)) {
UnaryFunction0D<float> *fun = ((BPy_UnaryFunction0DFloat *)obj1)->uf0D_float;
float res = integrate(*fun, it, it_end, t);
return PyFloat_FromDouble(res);
}
if (BPy_UnaryFunction0DUnsigned_Check(obj1)) {
UnaryFunction0D<uint> *fun = ((BPy_UnaryFunction0DUnsigned *)obj1)->uf0D_unsigned;
uint res = integrate(*fun, it, it_end, t);
return PyLong_FromLong(res);
}
string class_name(Py_TYPE(obj1)->tp_name);
PyErr_SetString(PyExc_TypeError, ("unsupported function type: " + class_name).c_str());
return nullptr;
}
/*-----------------------Integrator module docstring---------------------------------------*/
PyDoc_STRVAR(
/* Wrap. */
module_docstring,
"The Blender Freestyle.Integrator submodule\n"
"\n");
/*-----------------------Integrator module functions definitions---------------------------*/
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wcast-function-type"
# else
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
# endif
#endif
static PyMethodDef module_functions[] = {
{"integrate",
(PyCFunction)Integrator_integrate,
METH_VARARGS | METH_KEYWORDS,
Integrator_integrate_doc},
{nullptr, nullptr, 0, nullptr},
};
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic pop
# else
# pragma GCC diagnostic pop
# endif
#endif
/*-----------------------Integrator module definition--------------------------------------*/
static PyModuleDef module_definition = {
/*m_base*/ PyModuleDef_HEAD_INIT,
/*m_name*/ "Freestyle.Integrator",
/*m_doc*/ module_docstring,
/*m_size*/ -1,
/*m_methods*/ module_functions,
/*m_slots*/ nullptr,
/*m_traverse*/ nullptr,
/*m_clear*/ nullptr,
/*m_free*/ nullptr,
};
/*-----------------------BPy_IntegrationType type definition ------------------------------*/
PyDoc_STRVAR(
/* Wrap. */
IntegrationType_doc,
"Class hierarchy: int > :class:`IntegrationType`\n"
"\n"
"Different integration methods that can be invoked to integrate into a\n"
"single value the set of values obtained from each 0D element of an 1D\n"
"element.\n"
"\n"
".. attribute:: MEAN\n"
"\n"
" The value computed for the 1D element is the mean of the values\n"
" obtained for the 0D elements.\n"
"\n"
".. attribute:: MIN\n"
"\n"
" The value computed for the 1D element is the minimum of the values\n"
" obtained for the 0D elements.\n"
"\n"
".. attribute:: MAX\n"
"\n"
" The value computed for the 1D element is the maximum of the values\n"
" obtained for the 0D elements.\n"
"\n"
".. attribute:: FIRST\n"
"\n"
" The value computed for the 1D element is the first of the values\n"
" obtained for the 0D elements.\n"
"\n"
".. attribute:: LAST\n"
"\n"
" The value computed for the 1D element is the last of the values\n"
" obtained for the 0D elements.\n");
PyTypeObject IntegrationType_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "IntegrationType",
/*tp_basicsize*/ sizeof(PyLongObject),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ nullptr,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ nullptr,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ nullptr,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
/*tp_doc*/ IntegrationType_doc,
/*tp_traverse*/ nullptr,
/*tp_clear*/ nullptr,
/*tp_richcompare*/ nullptr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ nullptr,
/*tp_members*/ nullptr,
/*tp_getset*/ nullptr,
/*tp_base*/ &PyLong_Type,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ nullptr,
/*tp_alloc*/ nullptr,
/*tp_new*/ nullptr,
};
/*-----------------------BPy_IntegrationType instance definitions -------------------------*/
//-------------------MODULE INITIALIZATION--------------------------------
int IntegrationType_Init(PyObject *module)
{
PyObject *m, *d, *f;
if (module == nullptr) {
return -1;
}
if (PyType_Ready(&IntegrationType_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "IntegrationType", (PyObject *)&IntegrationType_Type);
#define ADD_TYPE_CONST(id) \
PyLong_subtype_add_to_dict( \
IntegrationType_Type.tp_dict, &IntegrationType_Type, STRINGIFY(id), id)
ADD_TYPE_CONST(MEAN);
ADD_TYPE_CONST(MIN);
ADD_TYPE_CONST(MAX);
ADD_TYPE_CONST(FIRST);
ADD_TYPE_CONST(LAST);
#undef ADD_TYPE_CONST
m = PyModule_Create(&module_definition);
if (m == nullptr) {
return -1;
}
PyModule_AddObjectRef(module, "Integrator", m);
// from Integrator import *
d = PyModule_GetDict(m);
for (PyMethodDef *p = module_functions; p->ml_name; p++) {
f = PyDict_GetItemString(d, p->ml_name);
PyModule_AddObjectRef(module, p->ml_name, f);
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,28 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
extern "C" {
#include <Python.h>
}
#include "../view_map/Interface1D.h"
///////////////////////////////////////////////////////////////////////////////////////////
extern PyTypeObject IntegrationType_Type;
#define BPy_IntegrationType_Check(v) \
(PyObject_IsInstance((PyObject *)v, (PyObject *)&IntegrationType_Type))
/*---------------------------Python BPy_IntegrationType visible prototypes-----------*/
int IntegrationType_Init(PyObject *module);
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,359 @@
/* SPDX-FileCopyrightText: 2004-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_Interface0D.h"
#include "BPy_Convert.h"
#include "BPy_Nature.h"
#include "Interface0D/BPy_CurvePoint.h"
#include "Interface0D/BPy_SVertex.h"
#include "Interface0D/BPy_ViewVertex.h"
#include "Interface0D/CurvePoint/BPy_StrokeVertex.h"
#include "Interface0D/ViewVertex/BPy_NonTVertex.h"
#include "Interface0D/ViewVertex/BPy_TVertex.h"
#include "Interface1D/BPy_FEdge.h"
using namespace Freestyle;
///////////////////////////////////////////////////////////////////////////////////////////
//-------------------MODULE INITIALIZATION--------------------------------
int Interface0D_Init(PyObject *module)
{
if (module == nullptr) {
return -1;
}
if (PyType_Ready(&Interface0D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "Interface0D", (PyObject *)&Interface0D_Type);
if (PyType_Ready(&CurvePoint_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "CurvePoint", (PyObject *)&CurvePoint_Type);
if (PyType_Ready(&SVertex_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "SVertex", (PyObject *)&SVertex_Type);
if (PyType_Ready(&ViewVertex_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "ViewVertex", (PyObject *)&ViewVertex_Type);
if (PyType_Ready(&StrokeVertex_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "StrokeVertex", (PyObject *)&StrokeVertex_Type);
if (PyType_Ready(&NonTVertex_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "NonTVertex", (PyObject *)&NonTVertex_Type);
if (PyType_Ready(&TVertex_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "TVertex", (PyObject *)&TVertex_Type);
SVertex_mathutils_register_callback();
StrokeVertex_mathutils_register_callback();
return 0;
}
/*----------------------Interface1D methods ----------------------------*/
PyDoc_STRVAR(
/* Wrap. */
Interface0D_doc,
"Base class for any 0D element.\n"
"\n"
".. method:: __init__()\n"
"\n"
" Default constructor.\n");
static int Interface0D_init(BPy_Interface0D *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {nullptr};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "", (char **)kwlist)) {
return -1;
}
self->if0D = new Interface0D();
self->borrowed = false;
return 0;
}
static void Interface0D_dealloc(BPy_Interface0D *self)
{
if (self->if0D && !self->borrowed) {
delete self->if0D;
}
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *Interface0D_repr(BPy_Interface0D *self)
{
return PyUnicode_FromFormat(
"type: %s - address: %p", self->if0D->getExactTypeName().c_str(), self->if0D);
}
PyDoc_STRVAR(
/* Wrap. */
Interface0D_get_fedge_doc,
".. method:: get_fedge(inter)\n"
"\n"
" Returns the FEdge that lies between this 0D element and the 0D\n"
" element given as the argument.\n"
"\n"
" :param inter: A 0D element.\n"
" :type inter: :class:`Interface0D`\n"
" :return: The FEdge lying between the two 0D elements.\n"
" :rtype: :class:`FEdge`\n");
static PyObject *Interface0D_get_fedge(BPy_Interface0D *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"inter", nullptr};
PyObject *py_if0D;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist, &Interface0D_Type, &py_if0D))
{
return nullptr;
}
FEdge *fe = self->if0D->getFEdge(*(((BPy_Interface0D *)py_if0D)->if0D));
if (PyErr_Occurred()) {
return nullptr;
}
if (fe) {
return Any_BPy_FEdge_from_FEdge(*fe);
}
Py_RETURN_NONE;
}
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wcast-function-type"
# else
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
# endif
#endif
static PyMethodDef BPy_Interface0D_methods[] = {
{"get_fedge",
(PyCFunction)Interface0D_get_fedge,
METH_VARARGS | METH_KEYWORDS,
Interface0D_get_fedge_doc},
{nullptr, nullptr, 0, nullptr},
};
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic pop
# else
# pragma GCC diagnostic pop
# endif
#endif
/*----------------------Interface1D get/setters ----------------------------*/
PyDoc_STRVAR(
/* Wrap. */
Interface0D_name_doc,
"The string of the name of this 0D element.\n"
"\n"
":type: str\n");
static PyObject *Interface0D_name_get(BPy_Interface0D *self, void * /*closure*/)
{
return PyUnicode_FromString(Py_TYPE(self)->tp_name);
}
PyDoc_STRVAR(
/* Wrap. */
Interface0D_point_3d_doc,
"The 3D point of this 0D element.\n"
"\n"
":type: :class:`mathutils.Vector`\n");
static PyObject *Interface0D_point_3d_get(BPy_Interface0D *self, void * /*closure*/)
{
Vec3f p(self->if0D->getPoint3D());
if (PyErr_Occurred()) {
return nullptr;
}
return Vector_from_Vec3f(p);
}
PyDoc_STRVAR(
/* Wrap. */
Interface0D_projected_x_doc,
"The X coordinate of the projected 3D point of this 0D element.\n"
"\n"
":type: float\n");
static PyObject *Interface0D_projected_x_get(BPy_Interface0D *self, void * /*closure*/)
{
real x = self->if0D->getProjectedX();
if (PyErr_Occurred()) {
return nullptr;
}
return PyFloat_FromDouble(x);
}
PyDoc_STRVAR(
/* Wrap. */
Interface0D_projected_y_doc,
"The Y coordinate of the projected 3D point of this 0D element.\n"
"\n"
":type: float\n");
static PyObject *Interface0D_projected_y_get(BPy_Interface0D *self, void * /*closure*/)
{
real y = self->if0D->getProjectedY();
if (PyErr_Occurred()) {
return nullptr;
}
return PyFloat_FromDouble(y);
}
PyDoc_STRVAR(
/* Wrap. */
Interface0D_projected_z_doc,
"The Z coordinate of the projected 3D point of this 0D element.\n"
"\n"
":type: float\n");
static PyObject *Interface0D_projected_z_get(BPy_Interface0D *self, void * /*closure*/)
{
real z = self->if0D->getProjectedZ();
if (PyErr_Occurred()) {
return nullptr;
}
return PyFloat_FromDouble(z);
}
PyDoc_STRVAR(
/* Wrap. */
Interface0D_point_2d_doc,
"The 2D point of this 0D element.\n"
"\n"
":type: :class:`mathutils.Vector`\n");
static PyObject *Interface0D_point_2d_get(BPy_Interface0D *self, void * /*closure*/)
{
Vec2f p(self->if0D->getPoint2D());
if (PyErr_Occurred()) {
return nullptr;
}
return Vector_from_Vec2f(p);
}
PyDoc_STRVAR(
/* Wrap. */
Interface0D_id_doc,
"The Id of this 0D element.\n"
"\n"
":type: :class:`Id`\n");
static PyObject *Interface0D_id_get(BPy_Interface0D *self, void * /*closure*/)
{
Id id(self->if0D->getId());
if (PyErr_Occurred()) {
return nullptr;
}
return BPy_Id_from_Id(id); // return a copy
}
PyDoc_STRVAR(
/* Wrap. */
Interface0D_nature_doc,
"The nature of this 0D element.\n"
"\n"
":type: :class:`Nature`\n");
static PyObject *Interface0D_nature_get(BPy_Interface0D *self, void * /*closure*/)
{
Nature::VertexNature nature = self->if0D->getNature();
if (PyErr_Occurred()) {
return nullptr;
}
return BPy_Nature_from_Nature(nature);
}
static PyGetSetDef BPy_Interface0D_getseters[] = {
{"name", (getter)Interface0D_name_get, (setter) nullptr, Interface0D_name_doc, nullptr},
{"point_3d",
(getter)Interface0D_point_3d_get,
(setter) nullptr,
Interface0D_point_3d_doc,
nullptr},
{"projected_x",
(getter)Interface0D_projected_x_get,
(setter) nullptr,
Interface0D_projected_x_doc,
nullptr},
{"projected_y",
(getter)Interface0D_projected_y_get,
(setter) nullptr,
Interface0D_projected_y_doc,
nullptr},
{"projected_z",
(getter)Interface0D_projected_z_get,
(setter) nullptr,
Interface0D_projected_z_doc,
nullptr},
{"point_2d",
(getter)Interface0D_point_2d_get,
(setter) nullptr,
Interface0D_point_2d_doc,
nullptr},
{"id", (getter)Interface0D_id_get, (setter) nullptr, Interface0D_id_doc, nullptr},
{"nature", (getter)Interface0D_nature_get, (setter) nullptr, Interface0D_nature_doc, nullptr},
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
};
/*-----------------------BPy_Interface0D type definition ------------------------------*/
PyTypeObject Interface0D_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "Interface0D",
/*tp_basicsize*/ sizeof(BPy_Interface0D),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ (destructor)Interface0D_dealloc,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ (reprfunc)Interface0D_repr,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ nullptr,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
/*tp_doc*/ Interface0D_doc,
/*tp_traverse*/ nullptr,
/*tp_clear*/ nullptr,
/*tp_richcompare*/ nullptr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ BPy_Interface0D_methods,
/*tp_members*/ nullptr,
/*tp_getset*/ BPy_Interface0D_getseters,
/*tp_base*/ nullptr,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ (initproc)Interface0D_init,
/*tp_alloc*/ nullptr,
/*tp_new*/ PyType_GenericNew,
};
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,35 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
extern "C" {
#include <Python.h>
}
#include "../view_map/Interface0D.h"
///////////////////////////////////////////////////////////////////////////////////////////
extern PyTypeObject Interface0D_Type;
#define BPy_Interface0D_Check(v) \
(PyObject_IsInstance((PyObject *)v, (PyObject *)&Interface0D_Type))
/*---------------------------Python BPy_Interface0D structure definition----------*/
struct BPy_Interface0D {
PyObject_HEAD
Freestyle::Interface0D *if0D;
bool borrowed; /* true if *if0D is a borrowed object */
};
/*---------------------------Python BPy_Interface0D visible prototypes-----------*/
int Interface0D_Init(PyObject *module);
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,387 @@
/* SPDX-FileCopyrightText: 2004-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_Interface1D.h"
#include "BPy_Convert.h"
#include "Interface1D/BPy_FEdge.h"
#include "Interface1D/BPy_FrsCurve.h"
#include "Interface1D/BPy_Stroke.h"
#include "Interface1D/BPy_ViewEdge.h"
#include "Interface1D/Curve/BPy_Chain.h"
#include "Interface1D/FEdge/BPy_FEdgeSharp.h"
#include "Interface1D/FEdge/BPy_FEdgeSmooth.h"
#include "BPy_MediumType.h"
using namespace Freestyle;
///////////////////////////////////////////////////////////////////////////////////////////
//-------------------MODULE INITIALIZATION--------------------------------
int Interface1D_Init(PyObject *module)
{
if (module == nullptr) {
return -1;
}
if (PyType_Ready(&Interface1D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "Interface1D", (PyObject *)&Interface1D_Type);
if (PyType_Ready(&FrsCurve_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "Curve", (PyObject *)&FrsCurve_Type);
if (PyType_Ready(&Chain_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "Chain", (PyObject *)&Chain_Type);
if (PyType_Ready(&FEdge_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "FEdge", (PyObject *)&FEdge_Type);
if (PyType_Ready(&FEdgeSharp_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "FEdgeSharp", (PyObject *)&FEdgeSharp_Type);
if (PyType_Ready(&FEdgeSmooth_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "FEdgeSmooth", (PyObject *)&FEdgeSmooth_Type);
if (PyType_Ready(&Stroke_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "Stroke", (PyObject *)&Stroke_Type);
#define ADD_TYPE_CONST(id) \
PyLong_subtype_add_to_dict(Stroke_Type.tp_dict, &MediumType_Type, STRINGIFY(id), Stroke::id)
ADD_TYPE_CONST(DRY_MEDIUM);
ADD_TYPE_CONST(HUMID_MEDIUM);
ADD_TYPE_CONST(OPAQUE_MEDIUM);
#undef ADD_TYPE_CONST
if (PyType_Ready(&ViewEdge_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "ViewEdge", (PyObject *)&ViewEdge_Type);
FEdgeSharp_mathutils_register_callback();
FEdgeSmooth_mathutils_register_callback();
return 0;
}
/*----------------------Interface1D methods ----------------------------*/
PyDoc_STRVAR(
/* Wrap. */
Interface1D_doc,
"Base class for any 1D element.\n"
"\n"
".. method:: __init__()\n"
"\n"
" Default constructor.\n");
static int Interface1D_init(BPy_Interface1D *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {nullptr};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "", (char **)kwlist)) {
return -1;
}
self->if1D = new Interface1D();
self->borrowed = false;
return 0;
}
static void Interface1D_dealloc(BPy_Interface1D *self)
{
if (self->if1D && !self->borrowed) {
delete self->if1D;
}
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *Interface1D_repr(BPy_Interface1D *self)
{
return PyUnicode_FromFormat(
"type: %s - address: %p", self->if1D->getExactTypeName().c_str(), self->if1D);
}
PyDoc_STRVAR(
/* Wrap. */
Interface1D_vertices_begin_doc,
".. method:: vertices_begin()\n"
"\n"
" Returns an iterator over the Interface1D vertices, pointing to the\n"
" first vertex.\n"
"\n"
" :return: An Interface0DIterator pointing to the first vertex.\n"
" :rtype: :class:`Interface0DIterator`\n");
static PyObject *Interface1D_vertices_begin(BPy_Interface1D *self)
{
Interface0DIterator if0D_it(self->if1D->verticesBegin());
return BPy_Interface0DIterator_from_Interface0DIterator(if0D_it, false);
}
PyDoc_STRVAR(
/* Wrap. */
Interface1D_vertices_end_doc,
".. method:: vertices_end()\n"
"\n"
" Returns an iterator over the Interface1D vertices, pointing after\n"
" the last vertex.\n"
"\n"
" :return: An Interface0DIterator pointing after the last vertex.\n"
" :rtype: :class:`Interface0DIterator`\n");
static PyObject *Interface1D_vertices_end(BPy_Interface1D *self)
{
Interface0DIterator if0D_it(self->if1D->verticesEnd());
return BPy_Interface0DIterator_from_Interface0DIterator(if0D_it, true);
}
PyDoc_STRVAR(
/* Wrap. */
Interface1D_points_begin_doc,
".. method:: points_begin(t=0.0)\n"
"\n"
" Returns an iterator over the Interface1D points, pointing to the\n"
" first point. The difference with vertices_begin() is that here we can\n"
" iterate over points of the 1D element at a any given sampling.\n"
" Indeed, for each iteration, a virtual point is created.\n"
"\n"
" :param t: A sampling with which we want to iterate over points of\n"
" this 1D element.\n"
" :type t: float\n"
" :return: An Interface0DIterator pointing to the first point.\n"
" :rtype: :class:`Interface0DIterator`\n");
static PyObject *Interface1D_points_begin(BPy_Interface1D *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"t", nullptr};
float f = 0.0f;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|f", (char **)kwlist, &f)) {
return nullptr;
}
Interface0DIterator if0D_it(self->if1D->pointsBegin(f));
return BPy_Interface0DIterator_from_Interface0DIterator(if0D_it, false);
}
PyDoc_STRVAR(
/* Wrap. */
Interface1D_points_end_doc,
".. method:: points_end(t=0.0)\n"
"\n"
" Returns an iterator over the Interface1D points, pointing after the\n"
" last point. The difference with vertices_end() is that here we can\n"
" iterate over points of the 1D element at a given sampling. Indeed,\n"
" for each iteration, a virtual point is created.\n"
"\n"
" :param t: A sampling with which we want to iterate over points of\n"
" this 1D element.\n"
" :type t: float\n"
" :return: An Interface0DIterator pointing after the last point.\n"
" :rtype: :class:`Interface0DIterator`\n");
static PyObject *Interface1D_points_end(BPy_Interface1D *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"t", nullptr};
float f = 0.0f;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|f", (char **)kwlist, &f)) {
return nullptr;
}
Interface0DIterator if0D_it(self->if1D->pointsEnd(f));
return BPy_Interface0DIterator_from_Interface0DIterator(if0D_it, true);
}
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wcast-function-type"
# else
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
# endif
#endif
static PyMethodDef BPy_Interface1D_methods[] = {
{"vertices_begin",
(PyCFunction)Interface1D_vertices_begin,
METH_NOARGS,
Interface1D_vertices_begin_doc},
{"vertices_end",
(PyCFunction)Interface1D_vertices_end,
METH_NOARGS,
Interface1D_vertices_end_doc},
{"points_begin",
(PyCFunction)Interface1D_points_begin,
METH_VARARGS | METH_KEYWORDS,
Interface1D_points_begin_doc},
{"points_end",
(PyCFunction)Interface1D_points_end,
METH_VARARGS | METH_KEYWORDS,
Interface1D_points_end_doc},
{nullptr, nullptr, 0, nullptr},
};
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic pop
# else
# pragma GCC diagnostic pop
# endif
#endif
/*----------------------Interface1D get/setters ----------------------------*/
PyDoc_STRVAR(
/* Wrap. */
Interface1D_name_doc,
"The string of the name of the 1D element.\n"
"\n"
":type: str\n");
static PyObject *Interface1D_name_get(BPy_Interface1D *self, void * /*closure*/)
{
return PyUnicode_FromString(Py_TYPE(self)->tp_name);
}
PyDoc_STRVAR(
/* Wrap. */
Interface1D_id_doc,
"The Id of this Interface1D.\n"
"\n"
":type: :class:`Id`\n");
static PyObject *Interface1D_id_get(BPy_Interface1D *self, void * /*closure*/)
{
Id id(self->if1D->getId());
if (PyErr_Occurred()) {
return nullptr;
}
return BPy_Id_from_Id(id); // return a copy
}
PyDoc_STRVAR(
/* Wrap. */
Interface1D_nature_doc,
"The nature of this Interface1D.\n"
"\n"
":type: :class:`Nature`\n");
static PyObject *Interface1D_nature_get(BPy_Interface1D *self, void * /*closure*/)
{
Nature::VertexNature nature = self->if1D->getNature();
if (PyErr_Occurred()) {
return nullptr;
}
return BPy_Nature_from_Nature(nature);
}
PyDoc_STRVAR(
/* Wrap. */
Interface1D_length_2d_doc,
"The 2D length of this Interface1D.\n"
"\n"
":type: float\n");
static PyObject *Interface1D_length_2d_get(BPy_Interface1D *self, void * /*closure*/)
{
real length = self->if1D->getLength2D();
if (PyErr_Occurred()) {
return nullptr;
}
return PyFloat_FromDouble(double(length));
}
PyDoc_STRVAR(
/* Wrap. */
Interface1D_time_stamp_doc,
"The time stamp of the 1D element, mainly used for selection.\n"
"\n"
":type: int\n");
static PyObject *Interface1D_time_stamp_get(BPy_Interface1D *self, void * /*closure*/)
{
return PyLong_FromLong(self->if1D->getTimeStamp());
}
static int Interface1D_time_stamp_set(BPy_Interface1D *self, PyObject *value, void * /*closure*/)
{
int timestamp;
if ((timestamp = PyLong_AsLong(value)) == -1 && PyErr_Occurred()) {
PyErr_SetString(PyExc_TypeError, "value must be a number");
return -1;
}
self->if1D->setTimeStamp(timestamp);
return 0;
}
static PyGetSetDef BPy_Interface1D_getseters[] = {
{"name", (getter)Interface1D_name_get, (setter) nullptr, Interface1D_name_doc, nullptr},
{"id", (getter)Interface1D_id_get, (setter) nullptr, Interface1D_id_doc, nullptr},
{"nature", (getter)Interface1D_nature_get, (setter) nullptr, Interface1D_nature_doc, nullptr},
{"length_2d",
(getter)Interface1D_length_2d_get,
(setter) nullptr,
Interface1D_length_2d_doc,
nullptr},
{"time_stamp",
(getter)Interface1D_time_stamp_get,
(setter)Interface1D_time_stamp_set,
Interface1D_time_stamp_doc,
nullptr},
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
};
/*-----------------------BPy_Interface1D type definition ------------------------------*/
PyTypeObject Interface1D_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "Interface1D",
/*tp_basicsize*/ sizeof(BPy_Interface1D),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ (destructor)Interface1D_dealloc,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ (reprfunc)Interface1D_repr,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ nullptr,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
/*tp_doc*/ Interface1D_doc,
/*tp_traverse*/ nullptr,
/*tp_clear*/ nullptr,
/*tp_richcompare*/ nullptr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ BPy_Interface1D_methods,
/*tp_members*/ nullptr,
/*tp_getset*/ BPy_Interface1D_getseters,
/*tp_base*/ nullptr,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ (initproc)Interface1D_init,
/*tp_alloc*/ nullptr,
/*tp_new*/ PyType_GenericNew,
};
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,35 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
extern "C" {
#include <Python.h>
}
#include "../view_map/Interface1D.h"
///////////////////////////////////////////////////////////////////////////////////////////
extern PyTypeObject Interface1D_Type;
#define BPy_Interface1D_Check(v) \
(PyObject_IsInstance((PyObject *)v, (PyObject *)&Interface1D_Type))
/*---------------------------Python BPy_Interface1D structure definition----------*/
struct BPy_Interface1D {
PyObject_HEAD
Freestyle::Interface1D *if1D;
bool borrowed; /* true if *if1D is a borrowed object */
};
/*---------------------------Python BPy_Interface1D visible prototypes-----------*/
int Interface1D_Init(PyObject *module);
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,268 @@
/* SPDX-FileCopyrightText: 2004-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_Iterator.h"
#include "BPy_Convert.h"
#include "Iterator/BPy_AdjacencyIterator.h"
#include "Iterator/BPy_ChainPredicateIterator.h"
#include "Iterator/BPy_ChainSilhouetteIterator.h"
#include "Iterator/BPy_ChainingIterator.h"
#include "Iterator/BPy_CurvePointIterator.h"
#include "Iterator/BPy_Interface0DIterator.h"
#include "Iterator/BPy_SVertexIterator.h"
#include "Iterator/BPy_StrokeVertexIterator.h"
#include "Iterator/BPy_ViewEdgeIterator.h"
#include "Iterator/BPy_orientedViewEdgeIterator.h"
using namespace Freestyle;
///////////////////////////////////////////////////////////////////////////////////////////
//-------------------MODULE INITIALIZATION--------------------------------
int Iterator_Init(PyObject *module)
{
if (module == nullptr) {
return -1;
}
if (PyType_Ready(&Iterator_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "Iterator", (PyObject *)&Iterator_Type);
if (PyType_Ready(&AdjacencyIterator_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "AdjacencyIterator", (PyObject *)&AdjacencyIterator_Type);
if (PyType_Ready(&Interface0DIterator_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "Interface0DIterator", (PyObject *)&Interface0DIterator_Type);
if (PyType_Ready(&CurvePointIterator_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "CurvePointIterator", (PyObject *)&CurvePointIterator_Type);
if (PyType_Ready(&StrokeVertexIterator_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "StrokeVertexIterator", (PyObject *)&StrokeVertexIterator_Type);
if (PyType_Ready(&SVertexIterator_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "SVertexIterator", (PyObject *)&SVertexIterator_Type);
if (PyType_Ready(&orientedViewEdgeIterator_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(
module, "orientedViewEdgeIterator", (PyObject *)&orientedViewEdgeIterator_Type);
if (PyType_Ready(&ViewEdgeIterator_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "ViewEdgeIterator", (PyObject *)&ViewEdgeIterator_Type);
if (PyType_Ready(&ChainingIterator_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "ChainingIterator", (PyObject *)&ChainingIterator_Type);
if (PyType_Ready(&ChainPredicateIterator_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(
module, "ChainPredicateIterator", (PyObject *)&ChainPredicateIterator_Type);
if (PyType_Ready(&ChainSilhouetteIterator_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(
module, "ChainSilhouetteIterator", (PyObject *)&ChainSilhouetteIterator_Type);
return 0;
}
//------------------------INSTANCE METHODS ----------------------------------
PyDoc_STRVAR(
/* Wrap. */
Iterator_doc,
"Base class to define iterators.\n"
"\n"
".. method:: __init__()\n"
"\n"
" Default constructor.\n");
static int Iterator_init(BPy_Iterator *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {nullptr};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "", (char **)kwlist)) {
return -1;
}
self->it = new Iterator();
return 0;
}
static void Iterator_dealloc(BPy_Iterator *self)
{
delete self->it;
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *Iterator_repr(BPy_Iterator *self)
{
return PyUnicode_FromFormat("type: %s - address: %p", Py_TYPE(self)->tp_name, self->it);
}
PyDoc_STRVAR(
/* Wrap. */
Iterator_increment_doc,
".. method:: increment()\n"
"\n"
" Makes the iterator point the next element.\n");
static PyObject *Iterator_increment(BPy_Iterator *self)
{
if (self->it->isEnd()) {
PyErr_SetString(PyExc_RuntimeError, "cannot increment any more");
return nullptr;
}
self->it->increment();
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
Iterator_decrement_doc,
".. method:: decrement()\n"
"\n"
" Makes the iterator point the previous element.\n");
static PyObject *Iterator_decrement(BPy_Iterator *self)
{
if (self->it->isBegin()) {
PyErr_SetString(PyExc_RuntimeError, "cannot decrement any more");
return nullptr;
}
self->it->decrement();
Py_RETURN_NONE;
}
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wcast-function-type"
# else
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
# endif
#endif
static PyMethodDef BPy_Iterator_methods[] = {
{"increment", (PyCFunction)Iterator_increment, METH_NOARGS, Iterator_increment_doc},
{"decrement", (PyCFunction)Iterator_decrement, METH_NOARGS, Iterator_decrement_doc},
{nullptr, nullptr, 0, nullptr},
};
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic pop
# else
# pragma GCC diagnostic pop
# endif
#endif
/*----------------------Iterator get/setters ----------------------------*/
PyDoc_STRVAR(
/* Wrap. */
Iterator_name_doc,
"The string of the name of this iterator.\n"
"\n"
":type: str\n");
static PyObject *Iterator_name_get(BPy_Iterator *self, void * /*closure*/)
{
return PyUnicode_FromString(Py_TYPE(self)->tp_name);
}
PyDoc_STRVAR(
/* Wrap. */
Iterator_is_begin_doc,
"True if the iterator points to the first element.\n"
"\n"
":type: bool\n");
static PyObject *Iterator_is_begin_get(BPy_Iterator *self, void * /*closure*/)
{
return PyBool_from_bool(self->it->isBegin());
}
PyDoc_STRVAR(
/* Wrap. */
Iterator_is_end_doc,
"True if the iterator points to the last element.\n"
"\n"
":type: bool\n");
static PyObject *Iterator_is_end_get(BPy_Iterator *self, void * /*closure*/)
{
return PyBool_from_bool(self->it->isEnd());
}
static PyGetSetDef BPy_Iterator_getseters[] = {
{"name", (getter)Iterator_name_get, (setter) nullptr, Iterator_name_doc, nullptr},
{"is_begin", (getter)Iterator_is_begin_get, (setter) nullptr, Iterator_is_begin_doc, nullptr},
{"is_end", (getter)Iterator_is_end_get, (setter) nullptr, Iterator_is_end_doc, nullptr},
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
};
/*-----------------------BPy_Iterator type definition ------------------------------*/
PyTypeObject Iterator_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "Iterator",
/*tp_basicsize*/ sizeof(BPy_Iterator),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ (destructor)Iterator_dealloc,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ (reprfunc)Iterator_repr,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ nullptr,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
/*tp_doc*/ Iterator_doc,
/*tp_traverse*/ nullptr,
/*tp_clear*/ nullptr,
/*tp_richcompare*/ nullptr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ BPy_Iterator_methods,
/*tp_members*/ nullptr,
/*tp_getset*/ BPy_Iterator_getseters,
/*tp_base*/ nullptr,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ (initproc)Iterator_init,
/*tp_alloc*/ nullptr,
/*tp_new*/ PyType_GenericNew,
};
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,33 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
extern "C" {
#include <Python.h>
}
#include "../system/Iterator.h"
///////////////////////////////////////////////////////////////////////////////////////////
extern PyTypeObject Iterator_Type;
#define BPy_Iterator_Check(v) (PyObject_IsInstance((PyObject *)v, (PyObject *)&Iterator_Type))
/*---------------------------Python BPy_Iterator structure definition----------*/
struct BPy_Iterator {
PyObject_HEAD
Freestyle::Iterator *it;
};
/*---------------------------Python BPy_Iterator visible prototypes-----------*/
int Iterator_Init(PyObject *module);
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,89 @@
/* SPDX-FileCopyrightText: 2004-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_MediumType.h"
#include "BPy_Convert.h"
using namespace Freestyle;
///////////////////////////////////////////////////////////////////////////////////////////
/*-----------------------BPy_MediumType type definition ------------------------------*/
PyDoc_STRVAR(
/* Wrap. */
MediumType_doc,
"Class hierarchy: int > :class:`MediumType`\n"
"\n"
"The different blending modes available to simulate the interaction\n"
"media-medium:\n"
"\n"
"* Stroke.DRY_MEDIUM: To simulate a dry medium such as Pencil or Charcoal.\n"
"* Stroke.HUMID_MEDIUM: To simulate ink painting (color subtraction blending).\n"
"* Stroke.OPAQUE_MEDIUM: To simulate an opaque medium (oil, spray...).\n");
PyTypeObject MediumType_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "MediumType",
/*tp_basicsize*/ sizeof(PyLongObject),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ nullptr,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ nullptr,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ nullptr,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
/*tp_doc*/ MediumType_doc,
/*tp_traverse*/ nullptr,
/*tp_clear*/ nullptr,
/*tp_richcompare*/ nullptr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ nullptr,
/*tp_members*/ nullptr,
/*tp_getset*/ nullptr,
/*tp_base*/ &PyLong_Type,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ nullptr,
/*tp_alloc*/ nullptr,
/*tp_new*/ nullptr,
};
/*-----------------------BPy_IntegrationType instance definitions -------------------------*/
//-------------------MODULE INITIALIZATION--------------------------------
int MediumType_Init(PyObject *module)
{
if (module == nullptr) {
return -1;
}
if (PyType_Ready(&MediumType_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "MediumType", (PyObject *)&MediumType_Type);
return 0;
}
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,32 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
extern "C" {
#include <Python.h>
}
#include "../stroke/Stroke.h"
///////////////////////////////////////////////////////////////////////////////////////////
extern PyTypeObject MediumType_Type;
#define BPy_MediumType_Check(v) (PyObject_IsInstance((PyObject *)v, (PyObject *)&MediumType_Type))
/*---------------------------Python BPy_MediumType structure definition----------*/
struct BPy_MediumType {
PyLongObject i;
};
/*---------------------------Python BPy_MediumType visible prototypes-----------*/
int MediumType_Init(PyObject *module);
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,265 @@
/* SPDX-FileCopyrightText: 2004-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_Nature.h"
#include "BPy_Convert.h"
using namespace Freestyle;
///////////////////////////////////////////////////////////////////////////////////////////
static PyObject *BPy_Nature_and(PyObject *a, PyObject *b);
static PyObject *BPy_Nature_xor(PyObject *a, PyObject *b);
static PyObject *BPy_Nature_or(PyObject *a, PyObject *b);
/*-----------------------BPy_Nature number method definitions --------------------*/
static PyNumberMethods nature_as_number = {
/*nb_add*/ nullptr,
/*nb_subtract*/ nullptr,
/*nb_multiply*/ nullptr,
/*nb_remainder*/ nullptr,
/*nb_divmod*/ nullptr,
/*nb_power*/ nullptr,
/*nb_negative*/ nullptr,
/*nb_positive*/ nullptr,
/*nb_absolute*/ nullptr,
/*nb_bool*/ nullptr,
/*nb_invert*/ nullptr,
/*nb_lshift*/ nullptr,
/*nb_rshift*/ nullptr,
/*nb_and*/ (binaryfunc)BPy_Nature_and,
/*nb_xor*/ (binaryfunc)BPy_Nature_xor,
/*nb_or*/ (binaryfunc)BPy_Nature_or,
/*nb_int*/ nullptr,
/*nb_reserved*/ nullptr,
/*nb_float*/ nullptr,
/*nb_inplace_add*/ nullptr,
/*nb_inplace_subtract*/ nullptr,
/*nb_inplace_multiply*/ nullptr,
/*nb_inplace_remainder*/ nullptr,
/*nb_inplace_power*/ nullptr,
/*nb_inplace_lshift*/ nullptr,
/*nb_inplace_rshift*/ nullptr,
/*nb_inplace_and*/ nullptr,
/*nb_inplace_xor*/ nullptr,
/*nb_inplace_or*/ nullptr,
/*nb_floor_divide*/ nullptr,
/*nb_true_divide*/ nullptr,
/*nb_inplace_floor_divide*/ nullptr,
/*nb_inplace_true_divide*/ nullptr,
/*nb_index*/ nullptr,
/*nb_matrix_multiply*/ nullptr,
/*nb_inplace_matrix_multiply*/ nullptr,
};
/*-----------------------BPy_Nature type definition ------------------------------*/
PyDoc_STRVAR(
/* Wrap. */
Nature_doc,
"Class hierarchy: int > :class:`Nature`\n"
"\n"
"Different possible natures of 0D and 1D elements of the ViewMap.\n"
"\n"
"Vertex natures:\n"
"\n"
".. attribute:: POINT\n"
"\n"
" True for any 0D element.\n"
"\n"
".. attribute:: S_VERTEX\n"
"\n"
" True for SVertex.\n"
"\n"
".. attribute:: VIEW_VERTEX\n"
"\n"
" True for ViewVertex.\n"
"\n"
".. attribute:: NON_T_VERTEX\n"
"\n"
" True for NonTVertex.\n"
"\n"
".. attribute:: T_VERTEX\n"
"\n"
" True for TVertex.\n"
"\n"
".. attribute:: CUSP\n"
"\n"
" True for CUSP.\n"
"\n"
"Edge natures:\n"
"\n"
".. attribute:: NO_FEATURE\n"
"\n"
" True for non feature edges (always false for 1D elements of the ViewMap).\n"
"\n"
".. attribute:: SILHOUETTE\n"
"\n"
" True for silhouettes.\n"
"\n"
".. attribute:: BORDER\n"
"\n"
" True for borders.\n"
"\n"
".. attribute:: CREASE\n"
"\n"
" True for creases.\n"
"\n"
".. attribute:: RIDGE\n"
"\n"
" True for ridges.\n"
"\n"
".. attribute:: VALLEY\n"
"\n"
" True for valleys.\n"
"\n"
".. attribute:: SUGGESTIVE_CONTOUR\n"
"\n"
" True for suggestive contours.\n"
"\n"
".. attribute:: MATERIAL_BOUNDARY\n"
"\n"
" True for edges at material boundaries.\n"
"\n"
".. attribute:: EDGE_MARK\n"
"\n"
" True for edges having user-defined edge marks.\n");
PyTypeObject Nature_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "Nature",
/*tp_basicsize*/ sizeof(PyLongObject),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ nullptr,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ nullptr,
/*tp_as_number*/ &nature_as_number,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ nullptr,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
/*tp_doc*/ Nature_doc,
/*tp_traverse*/ nullptr,
/*tp_clear*/ nullptr,
/*tp_richcompare*/ nullptr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ nullptr,
/*tp_members*/ nullptr,
/*tp_getset*/ nullptr,
/*tp_base*/ &PyLong_Type,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ nullptr,
/*tp_alloc*/ nullptr,
/*tp_new*/ nullptr,
};
/*-----------------------BPy_Nature instance definitions ----------------------------------*/
//-------------------MODULE INITIALIZATION--------------------------------
int Nature_Init(PyObject *module)
{
if (module == nullptr) {
return -1;
}
if (PyType_Ready(&Nature_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "Nature", (PyObject *)&Nature_Type);
#define ADD_TYPE_CONST(id) \
PyLong_subtype_add_to_dict(Nature_Type.tp_dict, &Nature_Type, STRINGIFY(id), Nature::id)
// VertexNature
ADD_TYPE_CONST(POINT);
ADD_TYPE_CONST(S_VERTEX);
ADD_TYPE_CONST(VIEW_VERTEX);
ADD_TYPE_CONST(NON_T_VERTEX);
ADD_TYPE_CONST(T_VERTEX);
ADD_TYPE_CONST(CUSP);
// EdgeNature
ADD_TYPE_CONST(NO_FEATURE);
ADD_TYPE_CONST(SILHOUETTE);
ADD_TYPE_CONST(BORDER);
ADD_TYPE_CONST(CREASE);
ADD_TYPE_CONST(RIDGE);
ADD_TYPE_CONST(VALLEY);
ADD_TYPE_CONST(SUGGESTIVE_CONTOUR);
ADD_TYPE_CONST(MATERIAL_BOUNDARY);
ADD_TYPE_CONST(EDGE_MARK);
#undef ADD_TYPE_CONST
return 0;
}
static PyObject *BPy_Nature_bitwise(PyObject *a, int op, PyObject *b)
{
long op1, op2, v;
if (!BPy_Nature_Check(a) || !BPy_Nature_Check(b)) {
PyErr_SetString(PyExc_TypeError, "operands must be a Nature object");
return nullptr;
}
if ((op1 = PyLong_AsLong(a)) == -1 && PyErr_Occurred()) {
PyErr_SetString(PyExc_ValueError, "operand 1: unexpected Nature value");
return nullptr;
}
if ((op2 = PyLong_AsLong(b)) == -1 && PyErr_Occurred()) {
PyErr_SetString(PyExc_ValueError, "operand 2: unexpected Nature value");
return nullptr;
}
switch (op) {
case '&':
v = op1 & op2;
break;
case '^':
v = op1 ^ op2;
break;
case '|':
v = op1 | op2;
break;
default:
PyErr_BadArgument();
return nullptr;
}
return PyLong_subtype_new(&Nature_Type, v);
}
static PyObject *BPy_Nature_and(PyObject *a, PyObject *b)
{
return BPy_Nature_bitwise(a, '&', b);
}
static PyObject *BPy_Nature_xor(PyObject *a, PyObject *b)
{
return BPy_Nature_bitwise(a, '^', b);
}
static PyObject *BPy_Nature_or(PyObject *a, PyObject *b)
{
return BPy_Nature_bitwise(a, '|', b);
}
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,32 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
extern "C" {
#include <Python.h>
}
#include "../winged_edge/Nature.h"
///////////////////////////////////////////////////////////////////////////////////////////
extern PyTypeObject Nature_Type;
#define BPy_Nature_Check(v) (PyObject_IsInstance((PyObject *)v, (PyObject *)&Nature_Type))
/*---------------------------Python BPy_Nature structure definition----------*/
struct BPy_Nature {
PyLongObject i;
};
/*---------------------------Python BPy_Nature visible prototypes-----------*/
int Nature_Init(PyObject *module);
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,854 @@
/* SPDX-FileCopyrightText: 2004-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_Operators.h"
#include "BPy_BinaryPredicate1D.h"
#include "BPy_Convert.h"
#include "BPy_StrokeShader.h"
#include "BPy_UnaryPredicate0D.h"
#include "BPy_UnaryPredicate1D.h"
#include "Iterator/BPy_ChainingIterator.h"
#include "Iterator/BPy_ViewEdgeIterator.h"
#include "UnaryFunction0D/BPy_UnaryFunction0DDouble.h"
#include "UnaryFunction1D/BPy_UnaryFunction1DVoid.h"
#include "BLI_sys_types.h"
#include <sstream>
using namespace Freestyle;
///////////////////////////////////////////////////////////////////////////////////////////
//-------------------MODULE INITIALIZATION--------------------------------
int Operators_Init(PyObject *module)
{
if (module == nullptr) {
return -1;
}
if (PyType_Ready(&Operators_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "Operators", (PyObject *)&Operators_Type);
return 0;
}
//------------------------INSTANCE METHODS ----------------------------------
PyDoc_STRVAR(
/* Wrap. */
Operators_doc,
"Class defining the operators used in a style module. There are five\n"
"types of operators: Selection, chaining, splitting, sorting and\n"
"creation. All these operators are user controlled through functors,\n"
"predicates and shaders that are taken as arguments.\n");
static void Operators_dealloc(BPy_Operators *self)
{
Py_TYPE(self)->tp_free((PyObject *)self);
}
PyDoc_STRVAR(
/* Wrap. */
Operators_select_doc,
".. staticmethod:: select(pred)\n"
"\n"
" Selects the ViewEdges of the ViewMap verifying a specified\n"
" condition.\n"
"\n"
" :param pred: The predicate expressing this condition.\n"
" :type pred: :class:`UnaryPredicate1D`\n");
static PyObject *Operators_select(BPy_Operators * /*self*/, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"pred", nullptr};
PyObject *obj = nullptr;
if (!PyArg_ParseTupleAndKeywords(
args, kwds, "O!", (char **)kwlist, &UnaryPredicate1D_Type, &obj))
{
return nullptr;
}
if (!((BPy_UnaryPredicate1D *)obj)->up1D) {
PyErr_SetString(PyExc_TypeError,
"Operators.select(): 1st argument: invalid UnaryPredicate1D object");
return nullptr;
}
if (Operators::select(*(((BPy_UnaryPredicate1D *)obj)->up1D)) < 0) {
if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_RuntimeError, "Operators.select() failed");
}
return nullptr;
}
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
Operators_chain_doc,
".. staticmethod:: chain(*args)\n"
"\n"
" Accepted call signatures:\n"
"\n"
" - ``chain(it, pred, modifier)``\n"
" - ``chain(it, pred)``\n"
"\n"
" Builds a set of chains from the current set of ViewEdges. Each\n"
" ViewEdge of the current list starts a new chain. The chaining\n"
" operator then iterates over the ViewEdges of the ViewMap using the\n"
" user specified iterator. This operator only iterates using the\n"
" increment operator and is therefore unidirectional.\n"
"\n"
" :param it: The iterator on the ViewEdges of the ViewMap. It contains\n"
" the chaining rule.\n"
" :type it: :class:`ViewEdgeIterator`\n"
" :param pred: The predicate on the ViewEdge that expresses the\n"
" stopping condition.\n"
" :type pred: :class:`UnaryPredicate1D`\n"
" :param modifier: A function that takes a ViewEdge as argument and\n"
" that is used to modify the processed ViewEdge state (the\n"
" timestamp incrementation is a typical illustration of such a modifier).\n"
" If this argument is not given, the time stamp is automatically managed.\n"
" :type modifier: :class:`UnaryFunction1DVoid`\n");
static PyObject *Operators_chain(BPy_Operators * /*self*/, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"it", "pred", "modifier", nullptr};
PyObject *obj1 = nullptr, *obj2 = nullptr, *obj3 = nullptr;
if (!PyArg_ParseTupleAndKeywords(args,
kwds,
"O!O!|O!",
(char **)kwlist,
&ChainingIterator_Type,
&obj1,
&UnaryPredicate1D_Type,
&obj2,
&UnaryFunction1DVoid_Type,
&obj3))
{
return nullptr;
}
if (!((BPy_ChainingIterator *)obj1)->c_it) {
PyErr_SetString(PyExc_TypeError,
"Operators.chain(): 1st argument: invalid ChainingIterator object");
return nullptr;
}
if (!((BPy_UnaryPredicate1D *)obj2)->up1D) {
PyErr_SetString(PyExc_TypeError,
"Operators.chain(): 2nd argument: invalid UnaryPredicate1D object");
return nullptr;
}
if (!obj3) {
if (Operators::chain(*(((BPy_ChainingIterator *)obj1)->c_it),
*(((BPy_UnaryPredicate1D *)obj2)->up1D)) < 0)
{
if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_RuntimeError, "Operators.chain() failed");
}
return nullptr;
}
}
else {
if (!((BPy_UnaryFunction1DVoid *)obj3)->uf1D_void) {
PyErr_SetString(PyExc_TypeError,
"Operators.chain(): 3rd argument: invalid UnaryFunction1DVoid object");
return nullptr;
}
if (Operators::chain(*(((BPy_ChainingIterator *)obj1)->c_it),
*(((BPy_UnaryPredicate1D *)obj2)->up1D),
*(((BPy_UnaryFunction1DVoid *)obj3)->uf1D_void)) < 0)
{
if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_RuntimeError, "Operators.chain() failed");
}
return nullptr;
}
}
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
Operators_bidirectional_chain_doc,
".. staticmethod:: bidirectional_chain(*args)\n"
"\n"
" Accepted call signatures:\n"
"\n"
" - ``bidirectional_chain(it, pred)``\n"
" - ``bidirectional_chain(it)``\n"
"\n"
" Builds a set of chains from the current set of ViewEdges. Each\n"
" ViewEdge of the current list potentially starts a new chain. The\n"
" chaining operator then iterates over the ViewEdges of the ViewMap\n"
" using the user specified iterator. This operator iterates both using\n"
" the increment and decrement operators and is therefore bidirectional.\n"
" This operator works with a ChainingIterator which contains the\n"
" chaining rules. It is this last one which can be told to chain only\n"
" edges that belong to the selection or not to process twice a ViewEdge\n"
" during the chaining. Each time a ViewEdge is added to a chain, its\n"
" chaining time stamp is incremented. This allows you to keep track of\n"
" the number of chains to which a ViewEdge belongs to.\n"
"\n"
" :param it: The ChainingIterator on the ViewEdges of the ViewMap. It\n"
" contains the chaining rule.\n"
" :type it: :class:`ChainingIterator`\n"
" :param pred: The predicate on the ViewEdge that expresses the stopping condition.\n"
" This parameter is optional, you make not want to pass a stopping criterion\n"
" when the stopping criterion is already contained in the iterator definition.\n"
" :type pred: :class:`UnaryPredicate1D`\n");
static PyObject *Operators_bidirectional_chain(BPy_Operators * /*self*/,
PyObject *args,
PyObject *kwds)
{
static const char *kwlist[] = {"it", "pred", nullptr};
PyObject *obj1 = nullptr, *obj2 = nullptr;
if (!PyArg_ParseTupleAndKeywords(args,
kwds,
"O!|O!",
(char **)kwlist,
&ChainingIterator_Type,
&obj1,
&UnaryPredicate1D_Type,
&obj2))
{
return nullptr;
}
if (!((BPy_ChainingIterator *)obj1)->c_it) {
PyErr_SetString(
PyExc_TypeError,
"Operators.bidirectional_chain(): 1st argument: invalid ChainingIterator object");
return nullptr;
}
if (!obj2) {
if (Operators::bidirectionalChain(*(((BPy_ChainingIterator *)obj1)->c_it)) < 0) {
if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_RuntimeError, "Operators.bidirectional_chain() failed");
}
return nullptr;
}
}
else {
if (!((BPy_UnaryPredicate1D *)obj2)->up1D) {
PyErr_SetString(
PyExc_TypeError,
"Operators.bidirectional_chain(): 2nd argument: invalid UnaryPredicate1D object");
return nullptr;
}
if (Operators::bidirectionalChain(*(((BPy_ChainingIterator *)obj1)->c_it),
*(((BPy_UnaryPredicate1D *)obj2)->up1D)) < 0)
{
if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_RuntimeError, "Operators.bidirectional_chain() failed");
}
return nullptr;
}
}
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
Operators_sequential_split_doc,
".. staticmethod:: sequential_split(*args, **kwargs)\n"
"\n"
" Accepted call signatures:\n"
"\n"
" - ``sequential_split(starting_pred, stopping_pred, sampling=0.0)``\n"
" - ``sequential_split(pred, sampling=0.0)``\n"
"\n"
" Splits each chain of the current set of chains in a sequential way.\n"
" The points of each chain are processed (with a specified sampling)\n"
" sequentially. The first point of the initial chain is the\n"
" first point of one of the resulting chains. The splitting ends when\n"
" no more chain can start.\n"
"\n"
" .. tip::\n"
"\n"
" By specifying a starting and stopping predicate allows\n"
" the chains to overlap rather than chains partitioning.\n"
"\n"
" :param starting_pred: The predicate on a point that expresses the\n"
" starting condition. Each time this condition is verified, a new chain begins\n"
" :type starting_pred: :class:`UnaryPredicate0D`\n"
" :param stopping_pred: The predicate on a point that expresses the\n"
" stopping condition. The chain ends as soon as this predicate is verified.\n"
" :type stopping_pred: :class:`UnaryPredicate0D`\n"
" :param pred: The predicate on a point that expresses the splitting condition.\n"
" Each time the condition is verified, the chain is split into two chains.\n"
" The resulting set of chains is a partition of the initial chain\n"
" :type pred: :class:`UnaryPredicate0D`\n"
" :param sampling: The resolution used to sample the chain for the\n"
" predicates evaluation. (The chain is not actually resampled;\n"
" a virtual point only progresses along the curve using this\n"
" resolution.)\n"
" :type sampling: float\n");
static PyObject *Operators_sequential_split(BPy_Operators * /*self*/,
PyObject *args,
PyObject *kwds)
{
static const char *kwlist_1[] = {"starting_pred", "stopping_pred", "sampling", nullptr};
static const char *kwlist_2[] = {"pred", "sampling", nullptr};
PyObject *obj1 = nullptr, *obj2 = nullptr;
float f = 0.0f;
if (PyArg_ParseTupleAndKeywords(args,
kwds,
"O!O!|f",
(char **)kwlist_1,
&UnaryPredicate0D_Type,
&obj1,
&UnaryPredicate0D_Type,
&obj2,
&f))
{
if (!((BPy_UnaryPredicate0D *)obj1)->up0D) {
PyErr_SetString(
PyExc_TypeError,
"Operators.sequential_split(): 1st argument: invalid UnaryPredicate0D object");
return nullptr;
}
if (!((BPy_UnaryPredicate0D *)obj2)->up0D) {
PyErr_SetString(
PyExc_TypeError,
"Operators.sequential_split(): 2nd argument: invalid UnaryPredicate0D object");
return nullptr;
}
if (Operators::sequentialSplit(*(((BPy_UnaryPredicate0D *)obj1)->up0D),
*(((BPy_UnaryPredicate0D *)obj2)->up0D),
f) < 0)
{
if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_RuntimeError, "Operators.sequential_split() failed");
}
return nullptr;
}
}
else if ((void)PyErr_Clear(),
(void)(f = 0.0f),
PyArg_ParseTupleAndKeywords(
args, kwds, "O!|f", (char **)kwlist_2, &UnaryPredicate0D_Type, &obj1, &f))
{
if (!((BPy_UnaryPredicate0D *)obj1)->up0D) {
PyErr_SetString(
PyExc_TypeError,
"Operators.sequential_split(): 1st argument: invalid UnaryPredicate0D object");
return nullptr;
}
if (Operators::sequentialSplit(*(((BPy_UnaryPredicate0D *)obj1)->up0D), f) < 0) {
if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_RuntimeError, "Operators.sequential_split() failed");
}
return nullptr;
}
}
else {
PyErr_SetString(PyExc_TypeError, "invalid argument(s)");
return nullptr;
}
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
Operators_recursive_split_doc,
".. staticmethod:: recursive_split(*args, **kwargs)\n"
"\n"
" Accepted call signatures:\n"
"\n"
" - ``recursive_split(func, pred_1d, sampling=0.0)``\n"
" - ``recursive_split(func, pred_0d, pred_1d, sampling=0.0)``\n"
"\n"
" Splits the current set of chains in a recursive way. We process the\n"
" points of each chain (with a specified sampling) to find the point\n"
" minimizing a specified function. The chain is split in two at this\n"
" point and the two new chains are processed in the same way. The\n"
" recursivity level is controlled through a predicate 1D that expresses\n"
" a stopping condition on the chain that is about to be processed.\n"
"\n"
" The user can also specify a 0D predicate to make a first selection on the points\n"
" that can potentially be split. A point that doesn't verify the 0D\n"
" predicate won't be candidate in realizing the min.\n"
"\n"
" :param func: The Unary Function evaluated at each point of the chain.\n"
" The splitting point is the point minimizing this function.\n"
" :type func: :class:`UnaryFunction0DDouble`\n"
" :param pred_0d: The Unary Predicate 0D used to select the candidate\n"
" points where the split can occur. For example, it is very likely\n"
" that would rather have your chain splitting around its middle\n"
" point than around one of its extremities. A 0D predicate working\n"
" on the curvilinear abscissa allows to add this kind of constraints.\n"
" :type pred_0d: :class:`UnaryPredicate0D`\n"
" :param pred_1d: The Unary Predicate expressing the recursivity stopping\n"
" condition. This predicate is evaluated for each curve before it\n"
" actually gets split. If pred_1d(chain) is true, the curve won't be\n"
" split anymore.\n"
" :type pred_1d: :class:`UnaryPredicate1D`\n"
" :param sampling: The resolution used to sample the chain for the\n"
" predicates evaluation. (The chain is not actually resampled; a\n"
" virtual point only progresses along the curve using this\n"
" resolution.)\n"
" :type sampling: float\n");
static PyObject *Operators_recursive_split(BPy_Operators * /*self*/,
PyObject *args,
PyObject *kwds)
{
static const char *kwlist_1[] = {"func", "pred_1d", "sampling", nullptr};
static const char *kwlist_2[] = {"func", "pred_0d", "pred_1d", "sampling", nullptr};
PyObject *obj1 = nullptr, *obj2 = nullptr, *obj3 = nullptr;
float f = 0.0f;
if (PyArg_ParseTupleAndKeywords(args,
kwds,
"O!O!|f",
(char **)kwlist_1,
&UnaryFunction0DDouble_Type,
&obj1,
&UnaryPredicate1D_Type,
&obj2,
&f))
{
if (!((BPy_UnaryFunction0DDouble *)obj1)->uf0D_double) {
PyErr_SetString(
PyExc_TypeError,
"Operators.recursive_split(): 1st argument: invalid UnaryFunction0DDouble object");
return nullptr;
}
if (!((BPy_UnaryPredicate1D *)obj2)->up1D) {
PyErr_SetString(
PyExc_TypeError,
"Operators.recursive_split(): 2nd argument: invalid UnaryPredicate1D object");
return nullptr;
}
if (Operators::recursiveSplit(*(((BPy_UnaryFunction0DDouble *)obj1)->uf0D_double),
*(((BPy_UnaryPredicate1D *)obj2)->up1D),
f) < 0)
{
if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_RuntimeError, "Operators.recursive_split() failed");
}
return nullptr;
}
}
else if ((void)PyErr_Clear(),
(void)(f = 0.0f),
PyArg_ParseTupleAndKeywords(args,
kwds,
"O!O!O!|f",
(char **)kwlist_2,
&UnaryFunction0DDouble_Type,
&obj1,
&UnaryPredicate0D_Type,
&obj2,
&UnaryPredicate1D_Type,
&obj3,
&f))
{
if (!((BPy_UnaryFunction0DDouble *)obj1)->uf0D_double) {
PyErr_SetString(
PyExc_TypeError,
"Operators.recursive_split(): 1st argument: invalid UnaryFunction0DDouble object");
return nullptr;
}
if (!((BPy_UnaryPredicate0D *)obj2)->up0D) {
PyErr_SetString(
PyExc_TypeError,
"Operators.recursive_split(): 2nd argument: invalid UnaryPredicate0D object");
return nullptr;
}
if (!((BPy_UnaryPredicate1D *)obj3)->up1D) {
PyErr_SetString(
PyExc_TypeError,
"Operators.recursive_split(): 3rd argument: invalid UnaryPredicate1D object");
return nullptr;
}
if (Operators::recursiveSplit(*(((BPy_UnaryFunction0DDouble *)obj1)->uf0D_double),
*(((BPy_UnaryPredicate0D *)obj2)->up0D),
*(((BPy_UnaryPredicate1D *)obj3)->up1D),
f) < 0)
{
if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_RuntimeError, "Operators.recursive_split() failed");
}
return nullptr;
}
}
else {
PyErr_SetString(PyExc_TypeError, "invalid argument(s)");
return nullptr;
}
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
Operators_sort_doc,
".. staticmethod:: sort(pred)\n"
"\n"
" Sorts the current set of chains (or viewedges) according to the\n"
" comparison predicate given as argument.\n"
"\n"
" :param pred: The binary predicate used for the comparison.\n"
" :type pred: :class:`BinaryPredicate1D`\n");
static PyObject *Operators_sort(BPy_Operators * /*self*/, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"pred", nullptr};
PyObject *obj = nullptr;
if (!PyArg_ParseTupleAndKeywords(
args, kwds, "O!", (char **)kwlist, &BinaryPredicate1D_Type, &obj))
{
return nullptr;
}
if (!((BPy_BinaryPredicate1D *)obj)->bp1D) {
PyErr_SetString(PyExc_TypeError,
"Operators.sort(): 1st argument: invalid BinaryPredicate1D object");
return nullptr;
}
if (Operators::sort(*(((BPy_BinaryPredicate1D *)obj)->bp1D)) < 0) {
if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_RuntimeError, "Operators.sort() failed");
}
return nullptr;
}
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
Operators_create_doc,
".. staticmethod:: create(pred, shaders)\n"
"\n"
" Creates and shades the strokes from the current set of chains. A\n"
" predicate can be specified to make a selection pass on the chains.\n"
"\n"
" :param pred: The predicate that a chain must verify in order to be\n"
" transform as a stroke.\n"
" :type pred: :class:`UnaryPredicate1D`\n"
" :param shaders: The list of shaders used to shade the strokes.\n"
" :type shaders: list[:class:`StrokeShader`]\n");
static PyObject *Operators_create(BPy_Operators * /*self*/, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"pred", "shaders", nullptr};
PyObject *obj1 = nullptr, *obj2 = nullptr;
if (!PyArg_ParseTupleAndKeywords(
args, kwds, "O!O!", (char **)kwlist, &UnaryPredicate1D_Type, &obj1, &PyList_Type, &obj2))
{
return nullptr;
}
if (!((BPy_UnaryPredicate1D *)obj1)->up1D) {
PyErr_SetString(PyExc_TypeError,
"Operators.create(): 1st argument: invalid UnaryPredicate1D object");
return nullptr;
}
vector<StrokeShader *> shaders;
shaders.reserve(PyList_Size(obj2));
for (int i = 0; i < PyList_Size(obj2); i++) {
PyObject *py_ss = PyList_GET_ITEM(obj2, i);
if (!BPy_StrokeShader_Check(py_ss)) {
PyErr_SetString(PyExc_TypeError,
"Operators.create(): 2nd argument must be a list of StrokeShader objects");
return nullptr;
}
StrokeShader *shader = ((BPy_StrokeShader *)py_ss)->ss;
if (!shader) {
stringstream ss;
ss << "Operators.create(): item " << (i + 1)
<< " of the shaders list is invalid likely due to missing call of "
"StrokeShader.__init__()";
PyErr_SetString(PyExc_TypeError, ss.str().c_str());
return nullptr;
}
shaders.push_back(shader);
}
if (Operators::create(*(((BPy_UnaryPredicate1D *)obj1)->up1D), shaders) < 0) {
if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_RuntimeError, "Operators.create() failed");
}
return nullptr;
}
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
Operators_reset_doc,
".. staticmethod:: reset(delete_strokes=True)\n"
"\n"
" Resets the line stylization process to the initial state. The results of\n"
" stroke creation are accumulated if **delete_strokes** is set to False.\n"
"\n"
" :param delete_strokes: Delete the strokes that are currently stored.\n"
" :type delete_strokes: bool\n");
static PyObject *Operators_reset(BPy_Operators * /*self*/, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"delete_strokes", nullptr};
PyObject *obj1 = nullptr;
if (PyArg_ParseTupleAndKeywords(args, kwds, "|O!", (char **)kwlist, &PyBool_Type, &obj1)) {
// true is the default
Operators::reset(obj1 ? bool_from_PyBool(obj1) : true);
}
else {
PyErr_SetString(PyExc_RuntimeError, "Operators.reset() failed");
return nullptr;
}
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
Operators_get_viewedge_from_index_doc,
".. staticmethod:: get_viewedge_from_index(i)\n"
"\n"
" Returns the ViewEdge at the index in the current set of ViewEdges.\n"
"\n"
" :param i: index (0 <= i < Operators.get_view_edges_size()).\n"
" :type i: int\n"
" :return: The ViewEdge object.\n"
" :rtype: :class:`ViewEdge`\n");
static PyObject *Operators_get_viewedge_from_index(BPy_Operators * /*self*/,
PyObject *args,
PyObject *kwds)
{
static const char *kwlist[] = {"i", nullptr};
uint i;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "I", (char **)kwlist, &i)) {
return nullptr;
}
if (i >= Operators::getViewEdgesSize()) {
PyErr_SetString(PyExc_IndexError, "index out of range");
return nullptr;
}
return BPy_ViewEdge_from_ViewEdge(*(Operators::getViewEdgeFromIndex(i)));
}
PyDoc_STRVAR(
/* Wrap. */
Operators_get_chain_from_index_doc,
".. staticmethod:: get_chain_from_index(i)\n"
"\n"
" Returns the Chain at the index in the current set of Chains.\n"
"\n"
" :param i: index (0 <= i < Operators.get_chains_size()).\n"
" :type i: int\n"
" :return: The Chain object.\n"
" :rtype: :class:`Chain`\n");
static PyObject *Operators_get_chain_from_index(BPy_Operators * /*self*/,
PyObject *args,
PyObject *kwds)
{
static const char *kwlist[] = {"i", nullptr};
uint i;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "I", (char **)kwlist, &i)) {
return nullptr;
}
if (i >= Operators::getChainsSize()) {
PyErr_SetString(PyExc_IndexError, "index out of range");
return nullptr;
}
return BPy_Chain_from_Chain(*(Operators::getChainFromIndex(i)));
}
PyDoc_STRVAR(
/* Wrap. */
Operators_get_stroke_from_index_doc,
".. staticmethod:: get_stroke_from_index(i)\n"
"\n"
" Returns the Stroke at the index in the current set of Strokes.\n"
"\n"
" :param i: index (0 <= i < Operators.get_strokes_size()).\n"
" :type i: int\n"
" :return: The Stroke object.\n"
" :rtype: :class:`Stroke`\n");
static PyObject *Operators_get_stroke_from_index(BPy_Operators * /*self*/,
PyObject *args,
PyObject *kwds)
{
static const char *kwlist[] = {"i", nullptr};
uint i;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "I", (char **)kwlist, &i)) {
return nullptr;
}
if (i >= Operators::getStrokesSize()) {
PyErr_SetString(PyExc_IndexError, "index out of range");
return nullptr;
}
return BPy_Stroke_from_Stroke(*(Operators::getStrokeFromIndex(i)));
}
PyDoc_STRVAR(
/* Wrap. */
Operators_get_view_edges_size_doc,
".. staticmethod:: get_view_edges_size()\n"
"\n"
" Returns the number of ViewEdges.\n"
"\n"
" :return: The number of ViewEdges.\n"
" :rtype: int\n");
static PyObject *Operators_get_view_edges_size(BPy_Operators * /*self*/)
{
return PyLong_FromLong(Operators::getViewEdgesSize());
}
PyDoc_STRVAR(
/* Wrap. */
Operators_get_chains_size_doc,
".. staticmethod:: get_chains_size()\n"
"\n"
" Returns the number of Chains.\n"
"\n"
" :return: The number of Chains.\n"
" :rtype: int\n");
static PyObject *Operators_get_chains_size(BPy_Operators * /*self*/)
{
return PyLong_FromLong(Operators::getChainsSize());
}
PyDoc_STRVAR(
/* Wrap. */
Operators_get_strokes_size_doc,
".. staticmethod:: get_strokes_size()\n"
"\n"
" Returns the number of Strokes.\n"
"\n"
" :return: The number of Strokes.\n"
" :rtype: int\n");
static PyObject *Operators_get_strokes_size(BPy_Operators * /*self*/)
{
return PyLong_FromLong(Operators::getStrokesSize());
}
/*----------------------Operators instance definitions ----------------------------*/
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wcast-function-type"
# else
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
# endif
#endif
static PyMethodDef BPy_Operators_methods[] = {
{"select",
(PyCFunction)Operators_select,
METH_VARARGS | METH_KEYWORDS | METH_STATIC,
Operators_select_doc},
{"chain",
(PyCFunction)Operators_chain,
METH_VARARGS | METH_KEYWORDS | METH_STATIC,
Operators_chain_doc},
{"bidirectional_chain",
(PyCFunction)Operators_bidirectional_chain,
METH_VARARGS | METH_KEYWORDS | METH_STATIC,
Operators_bidirectional_chain_doc},
{"sequential_split",
(PyCFunction)Operators_sequential_split,
METH_VARARGS | METH_KEYWORDS | METH_STATIC,
Operators_sequential_split_doc},
{"recursive_split",
(PyCFunction)Operators_recursive_split,
METH_VARARGS | METH_KEYWORDS | METH_STATIC,
Operators_recursive_split_doc},
{"sort",
(PyCFunction)Operators_sort,
METH_VARARGS | METH_KEYWORDS | METH_STATIC,
Operators_sort_doc},
{"create",
(PyCFunction)Operators_create,
METH_VARARGS | METH_KEYWORDS | METH_STATIC,
Operators_create_doc},
{"reset",
(PyCFunction)Operators_reset,
METH_VARARGS | METH_KEYWORDS | METH_STATIC,
Operators_reset_doc},
{"get_viewedge_from_index",
(PyCFunction)Operators_get_viewedge_from_index,
METH_VARARGS | METH_KEYWORDS | METH_STATIC,
Operators_get_viewedge_from_index_doc},
{"get_chain_from_index",
(PyCFunction)Operators_get_chain_from_index,
METH_VARARGS | METH_KEYWORDS | METH_STATIC,
Operators_get_chain_from_index_doc},
{"get_stroke_from_index",
(PyCFunction)Operators_get_stroke_from_index,
METH_VARARGS | METH_KEYWORDS | METH_STATIC,
Operators_get_stroke_from_index_doc},
{"get_view_edges_size",
(PyCFunction)Operators_get_view_edges_size,
METH_NOARGS | METH_STATIC,
Operators_get_view_edges_size_doc},
{"get_chains_size",
(PyCFunction)Operators_get_chains_size,
METH_NOARGS | METH_STATIC,
Operators_get_chains_size_doc},
{"get_strokes_size",
(PyCFunction)Operators_get_strokes_size,
METH_NOARGS | METH_STATIC,
Operators_get_strokes_size_doc},
{nullptr, nullptr, 0, nullptr},
};
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic pop
# else
# pragma GCC diagnostic pop
# endif
#endif
/*-----------------------BPy_Operators type definition ------------------------------*/
PyTypeObject Operators_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "Operators",
/*tp_basicsize*/ sizeof(BPy_Operators),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ (destructor)Operators_dealloc,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ nullptr,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ nullptr,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
/*tp_doc*/ Operators_doc,
/*tp_traverse*/ nullptr,
/*tp_clear*/ nullptr,
/*tp_richcompare*/ nullptr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ BPy_Operators_methods,
/*tp_members*/ nullptr,
/*tp_getset*/ nullptr,
/*tp_base*/ nullptr,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ nullptr,
/*tp_alloc*/ nullptr,
/*tp_new*/ PyType_GenericNew,
};
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,32 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
extern "C" {
#include <Python.h>
}
#include "../stroke/Operators.h"
///////////////////////////////////////////////////////////////////////////////////////////
extern PyTypeObject Operators_Type;
#define BPy_Operators_Check(v) (PyObject_IsInstance((PyObject *)v, (PyObject *)&Operators_Type))
/*---------------------------Python BPy_Operators structure definition----------*/
struct BPy_Operators {
PyObject_HEAD
};
/*---------------------------Python BPy_Operators visible prototypes-----------*/
int Operators_Init(PyObject *module);
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,338 @@
/* SPDX-FileCopyrightText: 2004-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_SShape.h"
#include "BPy_BBox.h"
#include "BPy_Convert.h"
#include "BPy_Id.h"
#include "Interface0D/BPy_SVertex.h"
#include "Interface1D/BPy_FEdge.h"
#include "BLI_sys_types.h"
#include "../generic/py_capi_utils.hh"
using namespace Freestyle;
///////////////////////////////////////////////////////////////////////////////////////////
//-------------------MODULE INITIALIZATION--------------------------------
int SShape_Init(PyObject *module)
{
if (module == nullptr) {
return -1;
}
if (PyType_Ready(&SShape_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "SShape", (PyObject *)&SShape_Type);
return 0;
}
/*----------------------SShape methods ----------------------------*/
PyDoc_STRVAR(
/* Wrap. */
SShape_doc,
"Class to define a feature shape. It is the gathering of feature\n"
"elements from an identified input shape.\n"
"\n"
".. method:: __init__(*args)\n"
"\n"
" Accepted call signatures:\n"
"\n"
" - ``__init__()``\n"
" - ``__init__(brother)``\n"
"\n"
" Creates a :class:`SShape` class using either a default constructor or copy constructor.\n"
"\n"
" :param brother: An SShape object.\n"
" :type brother: :class:`SShape`\n");
static int SShape_init(BPy_SShape *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"brother", nullptr};
PyObject *brother = nullptr;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!", (char **)kwlist, &SShape_Type, &brother)) {
return -1;
}
if (!brother) {
self->ss = new SShape();
}
else {
self->ss = new SShape(*(((BPy_SShape *)brother)->ss));
}
self->borrowed = false;
return 0;
}
static void SShape_dealloc(BPy_SShape *self)
{
if (self->ss && !self->borrowed) {
delete self->ss;
}
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *SShape_repr(BPy_SShape *self)
{
return PyUnicode_FromFormat("SShape - address: %p", self->ss);
}
static char SShape_add_edge_doc[] =
".. method:: add_edge(edge)\n"
"\n"
" Adds an FEdge to the list of FEdges.\n"
"\n"
" :param edge: An FEdge object.\n"
" :type edge: :class:`FEdge`\n";
static PyObject *SShape_add_edge(BPy_SShape *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"edge", nullptr};
PyObject *py_fe = nullptr;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist, &FEdge_Type, &py_fe)) {
return nullptr;
}
self->ss->AddEdge(((BPy_FEdge *)py_fe)->fe);
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
SShape_add_vertex_doc,
".. method:: add_vertex(vertex)\n"
"\n"
" Adds an SVertex to the list of SVertex of this Shape. The SShape\n"
" attribute of the SVertex is also set to this SShape.\n"
"\n"
" :param vertex: An SVertex object.\n"
" :type vertex: :class:`SVertex`\n");
static PyObject *SShape_add_vertex(BPy_SShape *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"edge", nullptr};
PyObject *py_sv = nullptr;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist, &SVertex_Type, &py_sv)) {
return nullptr;
}
self->ss->AddNewVertex(((BPy_SVertex *)py_sv)->sv);
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
SShape_compute_bbox_doc,
".. method:: compute_bbox()\n"
"\n"
" Compute the bbox of the SShape.\n");
static PyObject *SShape_compute_bbox(BPy_SShape *self)
{
self->ss->ComputeBBox();
Py_RETURN_NONE;
}
// const Material & material (uint i) const
// const vector< Material > & materials () const
// void SetMaterials (const vector< Material > &iMaterials)
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wcast-function-type"
# else
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
# endif
#endif
static PyMethodDef BPy_SShape_methods[] = {
{"add_edge", (PyCFunction)SShape_add_edge, METH_VARARGS | METH_KEYWORDS, SShape_add_edge_doc},
{"add_vertex",
(PyCFunction)SShape_add_vertex,
METH_VARARGS | METH_KEYWORDS,
SShape_add_vertex_doc},
{"compute_bbox", (PyCFunction)SShape_compute_bbox, METH_NOARGS, SShape_compute_bbox_doc},
{nullptr, nullptr, 0, nullptr},
};
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic pop
# else
# pragma GCC diagnostic pop
# endif
#endif
/*----------------------SShape get/setters ----------------------------*/
PyDoc_STRVAR(
/* Wrap. */
SShape_id_doc,
"The Id of this SShape.\n"
"\n"
":type: :class:`Id`\n");
static PyObject *SShape_id_get(BPy_SShape *self, void * /*closure*/)
{
Id id(self->ss->getId());
return BPy_Id_from_Id(id); // return a copy
}
static int SShape_id_set(BPy_SShape *self, PyObject *value, void * /*closure*/)
{
if (!BPy_Id_Check(value)) {
PyErr_SetString(PyExc_TypeError, "value must be an Id");
return -1;
}
self->ss->setId(*(((BPy_Id *)value)->id));
return 0;
}
PyDoc_STRVAR(
/* Wrap. */
SShape_name_doc,
"The name of the SShape.\n"
"\n"
":type: str\n");
static PyObject *SShape_name_get(BPy_SShape *self, void * /*closure*/)
{
return blender::PyC_UnicodeFromStdStr(self->ss->getName());
}
static int SShape_name_set(BPy_SShape *self, PyObject *value, void * /*closure*/)
{
if (!PyUnicode_Check(value)) {
PyErr_SetString(PyExc_TypeError, "value must be a string");
return -1;
}
const char *name = PyUnicode_AsUTF8(value);
self->ss->setName(name);
return 0;
}
PyDoc_STRVAR(
/* Wrap. */
SShape_bbox_doc,
"The bounding box of the SShape.\n"
"\n"
":type: :class:`BBox`\n");
static PyObject *SShape_bbox_get(BPy_SShape *self, void * /*closure*/)
{
BBox<Vec3r> bb(self->ss->bbox());
return BPy_BBox_from_BBox(bb); // return a copy
}
static int SShape_bbox_set(BPy_SShape *self, PyObject *value, void * /*closure*/)
{
if (!BPy_BBox_Check(value)) {
PyErr_SetString(PyExc_TypeError, "value must be a BBox");
return -1;
}
self->ss->setBBox(*(((BPy_BBox *)value)->bb));
return 0;
}
PyDoc_STRVAR(
/* Wrap. */
SShape_vertices_doc,
"The list of vertices constituting this SShape.\n"
"\n"
":type: list[:class:`SVertex`]\n");
static PyObject *SShape_vertices_get(BPy_SShape *self, void * /*closure*/)
{
vector<SVertex *> vertices = self->ss->getVertexList();
vector<SVertex *>::iterator it;
PyObject *py_vertices = PyList_New(vertices.size());
uint i = 0;
for (it = vertices.begin(); it != vertices.end(); it++) {
PyList_SET_ITEM(py_vertices, i++, BPy_SVertex_from_SVertex(*(*it)));
}
return py_vertices;
}
PyDoc_STRVAR(
/* Wrap. */
SShape_edges_doc,
"The list of edges constituting this SShape.\n"
"\n"
":type: list[:class:`FEdge`]\n");
static PyObject *SShape_edges_get(BPy_SShape *self, void * /*closure*/)
{
vector<FEdge *> edges = self->ss->getEdgeList();
vector<FEdge *>::iterator it;
PyObject *py_edges = PyList_New(edges.size());
uint i = 0;
for (it = edges.begin(); it != edges.end(); it++) {
PyList_SET_ITEM(py_edges, i++, Any_BPy_FEdge_from_FEdge(*(*it)));
}
return py_edges;
}
static PyGetSetDef BPy_SShape_getseters[] = {
{"id", (getter)SShape_id_get, (setter)SShape_id_set, SShape_id_doc, nullptr},
{"name", (getter)SShape_name_get, (setter)SShape_name_set, SShape_name_doc, nullptr},
{"bbox", (getter)SShape_bbox_get, (setter)SShape_bbox_set, SShape_bbox_doc, nullptr},
{"edges", (getter)SShape_edges_get, (setter) nullptr, SShape_edges_doc, nullptr},
{"vertices", (getter)SShape_vertices_get, (setter) nullptr, SShape_vertices_doc, nullptr},
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
};
/*-----------------------BPy_SShape type definition ------------------------------*/
PyTypeObject SShape_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "SShape",
/*tp_basicsize*/ sizeof(BPy_SShape),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ (destructor)SShape_dealloc,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ (reprfunc)SShape_repr,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ nullptr,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
/*tp_doc*/ SShape_doc,
/*tp_traverse*/ nullptr,
/*tp_clear*/ nullptr,
/*tp_richcompare*/ nullptr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ BPy_SShape_methods,
/*tp_members*/ nullptr,
/*tp_getset*/ BPy_SShape_getseters,
/*tp_base*/ nullptr,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ (initproc)SShape_init,
/*tp_alloc*/ nullptr,
/*tp_new*/ PyType_GenericNew,
};
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,34 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
extern "C" {
#include <Python.h>
}
#include "../view_map/Silhouette.h"
///////////////////////////////////////////////////////////////////////////////////////////
extern PyTypeObject SShape_Type;
#define BPy_SShape_Check(v) (PyObject_IsInstance((PyObject *)v, (PyObject *)&SShape_Type))
/*---------------------------Python BPy_SShape structure definition----------*/
struct BPy_SShape {
PyObject_HEAD
Freestyle::SShape *ss;
bool borrowed; /* true if *ss is a borrowed object */
};
/*---------------------------Python BPy_SShape visible prototypes-----------*/
int SShape_Init(PyObject *module);
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,750 @@
/* SPDX-FileCopyrightText: 2004-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_StrokeAttribute.h"
#include "BPy_Convert.h"
#include "../generic/py_capi_utils.hh"
using namespace Freestyle;
///////////////////////////////////////////////////////////////////////////////////////////
//-------------------MODULE INITIALIZATION--------------------------------
int StrokeAttribute_Init(PyObject *module)
{
if (module == nullptr) {
return -1;
}
if (PyType_Ready(&StrokeAttribute_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "StrokeAttribute", (PyObject *)&StrokeAttribute_Type);
StrokeAttribute_mathutils_register_callback();
return 0;
}
//------------------------INSTANCE METHODS ----------------------------------
PyDoc_STRVAR(
/* Wrap. */
StrokeAttribute_doc,
"Class to define a set of attributes associated with a :class:`StrokeVertex`.\n"
"The attribute set stores the color, alpha and thickness values for a Stroke\n"
"Vertex.\n"
"\n"
".. method:: __init__(*args)\n"
"\n"
" Accepted call signatures:\n"
"\n"
" - ``__init__()``\n"
" - ``__init__(brother)``\n"
" - ``__init__(red, green, blue, alpha, thickness_right, thickness_left)``\n"
" - ``__init__(attribute1, attribute2, t)``\n"
"\n"
" Creates a :class:`StrokeAttribute` object using either a default constructor,\n"
" copy constructor, overloaded constructor, or and interpolation constructor\n"
" to interpolate between two :class:`StrokeAttribute` objects.\n"
"\n"
" :param brother: A StrokeAttribute object to be used as a copy constructor.\n"
" :type brother: :class:`StrokeAttribute`\n"
" :param red: Red component of a stroke color.\n"
" :type red: float\n"
" :param green: Green component of a stroke color.\n"
" :type green: float\n"
" :param blue: Blue component of a stroke color.\n"
" :type blue: float\n"
" :param alpha: Alpha component of a stroke color.\n"
" :type alpha: float\n"
" :param thickness_right: Stroke thickness on the right.\n"
" :type thickness_right: float\n"
" :param thickness_left: Stroke thickness on the left.\n"
" :type thickness_left: float\n"
" :param attribute1: The first StrokeAttribute object.\n"
" :type attribute1: :class:`StrokeAttribute`\n"
" :param attribute2: The second StrokeAttribute object.\n"
" :type attribute2: :class:`StrokeAttribute`\n"
" :param t: The interpolation parameter (0 <= t <= 1).\n"
" :type t: float\n");
static int StrokeAttribute_init(BPy_StrokeAttribute *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist_1[] = {"brother", nullptr};
static const char *kwlist_2[] = {"attribute1", "attribute2", "t", nullptr};
static const char *kwlist_3[] = {
"red", "green", "blue", "alpha", "thickness_right", "thickness_left", nullptr};
PyObject *obj1 = nullptr, *obj2 = nullptr;
float red, green, blue, alpha, thickness_right, thickness_left, t;
if (PyArg_ParseTupleAndKeywords(
args, kwds, "|O!", (char **)kwlist_1, &StrokeAttribute_Type, &obj1))
{
if (!obj1) {
self->sa = new StrokeAttribute();
}
else {
self->sa = new StrokeAttribute(*(((BPy_StrokeAttribute *)obj1)->sa));
}
}
else if ((void)PyErr_Clear(),
PyArg_ParseTupleAndKeywords(args,
kwds,
"O!O!f",
(char **)kwlist_2,
&StrokeAttribute_Type,
&obj1,
&StrokeAttribute_Type,
&obj2,
&t))
{
self->sa = new StrokeAttribute(
*(((BPy_StrokeAttribute *)obj1)->sa), *(((BPy_StrokeAttribute *)obj2)->sa), t);
}
else if ((void)PyErr_Clear(),
PyArg_ParseTupleAndKeywords(args,
kwds,
"ffffff",
(char **)kwlist_3,
&red,
&green,
&blue,
&alpha,
&thickness_right,
&thickness_left))
{
self->sa = new StrokeAttribute(red, green, blue, alpha, thickness_right, thickness_left);
}
else {
PyErr_SetString(PyExc_TypeError, "invalid argument(s)");
return -1;
}
self->borrowed = false;
return 0;
}
static void StrokeAttribute_dealloc(BPy_StrokeAttribute *self)
{
if (self->sa && !self->borrowed) {
delete self->sa;
}
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *StrokeAttribute_repr(BPy_StrokeAttribute *self)
{
stringstream repr("StrokeAttribute:");
repr << " r: " << self->sa->getColorR() << " g: " << self->sa->getColorG()
<< " b: " << self->sa->getColorB() << " a: " << self->sa->getAlpha()
<< " - R: " << self->sa->getThicknessR() << " L: " << self->sa->getThicknessL();
return blender::PyC_UnicodeFromStdStr(repr.str());
}
PyDoc_STRVAR(
/* Wrap. */
StrokeAttribute_get_attribute_real_doc,
".. method:: get_attribute_real(name)\n"
"\n"
" Returns an attribute of float type.\n"
"\n"
" :param name: The name of the attribute.\n"
" :type name: str\n"
" :return: The attribute value.\n"
" :rtype: float\n");
static PyObject *StrokeAttribute_get_attribute_real(BPy_StrokeAttribute *self,
PyObject *args,
PyObject *kwds)
{
static const char *kwlist[] = {"name", nullptr};
char *attr;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", (char **)kwlist, &attr)) {
return nullptr;
}
double a = self->sa->getAttributeReal(attr);
return PyFloat_FromDouble(a);
}
PyDoc_STRVAR(
/* Wrap. */
StrokeAttribute_get_attribute_vec2_doc,
".. method:: get_attribute_vec2(name)\n"
"\n"
" Returns an attribute of two-dimensional vector type.\n"
"\n"
" :param name: The name of the attribute.\n"
" :type name: str\n"
" :return: The attribute value.\n"
" :rtype: :class:`mathutils.Vector`\n");
static PyObject *StrokeAttribute_get_attribute_vec2(BPy_StrokeAttribute *self,
PyObject *args,
PyObject *kwds)
{
static const char *kwlist[] = {"name", nullptr};
char *attr;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", (char **)kwlist, &attr)) {
return nullptr;
}
Vec2f a = self->sa->getAttributeVec2f(attr);
return Vector_from_Vec2f(a);
}
PyDoc_STRVAR(
/* Wrap. */
StrokeAttribute_get_attribute_vec3_doc,
".. method:: get_attribute_vec3(name)\n"
"\n"
" Returns an attribute of three-dimensional vector type.\n"
"\n"
" :param name: The name of the attribute.\n"
" :type name: str\n"
" :return: The attribute value.\n"
" :rtype: :class:`mathutils.Vector`\n");
static PyObject *StrokeAttribute_get_attribute_vec3(BPy_StrokeAttribute *self,
PyObject *args,
PyObject *kwds)
{
static const char *kwlist[] = {"name", nullptr};
char *attr;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", (char **)kwlist, &attr)) {
return nullptr;
}
Vec3f a = self->sa->getAttributeVec3f(attr);
return Vector_from_Vec3f(a);
}
PyDoc_STRVAR(
/* Wrap. */
StrokeAttribute_has_attribute_real_doc,
".. method:: has_attribute_real(name)\n"
"\n"
" Checks whether the attribute name of float type is available.\n"
"\n"
" :param name: The name of the attribute.\n"
" :type name: str\n"
" :return: True if the attribute is available.\n"
" :rtype: bool\n");
static PyObject *StrokeAttribute_has_attribute_real(BPy_StrokeAttribute *self,
PyObject *args,
PyObject *kwds)
{
static const char *kwlist[] = {"name", nullptr};
char *attr;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", (char **)kwlist, &attr)) {
return nullptr;
}
return PyBool_from_bool(self->sa->isAttributeAvailableReal(attr));
}
PyDoc_STRVAR(
/* Wrap. */
StrokeAttribute_has_attribute_vec2_doc,
".. method:: has_attribute_vec2(name)\n"
"\n"
" Checks whether the attribute name of two-dimensional vector type\n"
" is available.\n"
"\n"
" :param name: The name of the attribute.\n"
" :type name: str\n"
" :return: True if the attribute is available.\n"
" :rtype: bool\n");
static PyObject *StrokeAttribute_has_attribute_vec2(BPy_StrokeAttribute *self,
PyObject *args,
PyObject *kwds)
{
static const char *kwlist[] = {"name", nullptr};
char *attr;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", (char **)kwlist, &attr)) {
return nullptr;
}
return PyBool_from_bool(self->sa->isAttributeAvailableVec2f(attr));
}
PyDoc_STRVAR(
/* Wrap. */
StrokeAttribute_has_attribute_vec3_doc,
".. method:: has_attribute_vec3(name)\n"
"\n"
" Checks whether the attribute name of three-dimensional vector\n"
" type is available.\n"
"\n"
" :param name: The name of the attribute.\n"
" :type name: str\n"
" :return: True if the attribute is available.\n"
" :rtype: bool\n");
static PyObject *StrokeAttribute_has_attribute_vec3(BPy_StrokeAttribute *self,
PyObject *args,
PyObject *kwds)
{
static const char *kwlist[] = {"name", nullptr};
char *attr;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", (char **)kwlist, &attr)) {
return nullptr;
}
return PyBool_from_bool(self->sa->isAttributeAvailableVec3f(attr));
}
PyDoc_STRVAR(
/* Wrap. */
StrokeAttribute_set_attribute_real_doc,
".. method:: set_attribute_real(name, value)\n"
"\n"
" Adds a user-defined attribute of float type. If there is no\n"
" attribute of the given name, it is added. Otherwise, the new value\n"
" replaces the old one.\n"
"\n"
" :param name: The name of the attribute.\n"
" :type name: str\n"
" :param value: The attribute value.\n"
" :type value: float\n");
static PyObject *StrokeAttribute_set_attribute_real(BPy_StrokeAttribute *self,
PyObject *args,
PyObject *kwds)
{
static const char *kwlist[] = {"name", "value", nullptr};
char *s = nullptr;
double d = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "sd", (char **)kwlist, &s, &d)) {
return nullptr;
}
self->sa->setAttributeReal(s, d);
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
StrokeAttribute_set_attribute_vec2_doc,
".. method:: set_attribute_vec2(name, value)\n"
"\n"
" Adds a user-defined attribute of two-dimensional vector type. If\n"
" there is no attribute of the given name, it is added. Otherwise,\n"
" the new value replaces the old one.\n"
"\n"
" :param name: The name of the attribute.\n"
" :type name: str\n"
" :param value: The attribute value.\n"
" :type value: :class:`mathutils.Vector` | tuple[float, float, float] | list[float]\n");
static PyObject *StrokeAttribute_set_attribute_vec2(BPy_StrokeAttribute *self,
PyObject *args,
PyObject *kwds)
{
static const char *kwlist[] = {"name", "value", nullptr};
char *s;
PyObject *obj = nullptr;
Vec2f vec;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO", (char **)kwlist, &s, &obj)) {
return nullptr;
}
if (!Vec2f_ptr_from_PyObject(obj, vec)) {
PyErr_SetString(PyExc_TypeError,
"argument 2 must be a 2D vector (either a list of 2 elements or Vector)");
return nullptr;
}
self->sa->setAttributeVec2f(s, vec);
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
StrokeAttribute_set_attribute_vec3_doc,
".. method:: set_attribute_vec3(name, value)\n"
"\n"
" Adds a user-defined attribute of three-dimensional vector type.\n"
" If there is no attribute of the given name, it is added.\n"
" Otherwise, the new value replaces the old one.\n"
"\n"
" :param name: The name of the attribute.\n"
" :type name: str\n"
" :param value: The attribute value as a 3D vector.\n"
" :type value: :class:`mathutils.Vector` | tuple[float, float, float] | list[float]\n");
static PyObject *StrokeAttribute_set_attribute_vec3(BPy_StrokeAttribute *self,
PyObject *args,
PyObject *kwds)
{
static const char *kwlist[] = {"name", "value", nullptr};
char *s;
PyObject *obj = nullptr;
Vec3f vec;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO", (char **)kwlist, &s, &obj)) {
return nullptr;
}
if (!Vec3f_ptr_from_PyObject(obj, vec)) {
PyErr_SetString(PyExc_TypeError,
"argument 2 must be a 3D vector (either a list of 3 elements or Vector)");
return nullptr;
}
self->sa->setAttributeVec3f(s, vec);
Py_RETURN_NONE;
}
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wcast-function-type"
# else
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
# endif
#endif
static PyMethodDef BPy_StrokeAttribute_methods[] = {
{"get_attribute_real",
(PyCFunction)StrokeAttribute_get_attribute_real,
METH_VARARGS | METH_KEYWORDS,
StrokeAttribute_get_attribute_real_doc},
{"get_attribute_vec2",
(PyCFunction)StrokeAttribute_get_attribute_vec2,
METH_VARARGS | METH_KEYWORDS,
StrokeAttribute_get_attribute_vec2_doc},
{"get_attribute_vec3",
(PyCFunction)StrokeAttribute_get_attribute_vec3,
METH_VARARGS | METH_KEYWORDS,
StrokeAttribute_get_attribute_vec3_doc},
{"has_attribute_real",
(PyCFunction)StrokeAttribute_has_attribute_real,
METH_VARARGS | METH_KEYWORDS,
StrokeAttribute_has_attribute_real_doc},
{"has_attribute_vec2",
(PyCFunction)StrokeAttribute_has_attribute_vec2,
METH_VARARGS | METH_KEYWORDS,
StrokeAttribute_has_attribute_vec2_doc},
{"has_attribute_vec3",
(PyCFunction)StrokeAttribute_has_attribute_vec3,
METH_VARARGS | METH_KEYWORDS,
StrokeAttribute_has_attribute_vec3_doc},
{"set_attribute_real",
(PyCFunction)StrokeAttribute_set_attribute_real,
METH_VARARGS | METH_KEYWORDS,
StrokeAttribute_set_attribute_real_doc},
{"set_attribute_vec2",
(PyCFunction)StrokeAttribute_set_attribute_vec2,
METH_VARARGS | METH_KEYWORDS,
StrokeAttribute_set_attribute_vec2_doc},
{"set_attribute_vec3",
(PyCFunction)StrokeAttribute_set_attribute_vec3,
METH_VARARGS | METH_KEYWORDS,
StrokeAttribute_set_attribute_vec3_doc},
{nullptr, nullptr, 0, nullptr},
};
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic pop
# else
# pragma GCC diagnostic pop
# endif
#endif
/*----------------------mathutils callbacks ----------------------------*/
/* subtype */
#define MATHUTILS_SUBTYPE_COLOR 1
#define MATHUTILS_SUBTYPE_THICKNESS 2
static int StrokeAttribute_mathutils_check(blender::BaseMathObject *bmo)
{
if (!BPy_StrokeAttribute_Check(bmo->cb_user)) {
return -1;
}
return 0;
}
static int StrokeAttribute_mathutils_get(blender::BaseMathObject *bmo, int subtype)
{
BPy_StrokeAttribute *self = (BPy_StrokeAttribute *)bmo->cb_user;
switch (subtype) {
case MATHUTILS_SUBTYPE_COLOR:
bmo->data[0] = self->sa->getColorR();
bmo->data[1] = self->sa->getColorG();
bmo->data[2] = self->sa->getColorB();
break;
case MATHUTILS_SUBTYPE_THICKNESS:
bmo->data[0] = self->sa->getThicknessR();
bmo->data[1] = self->sa->getThicknessL();
break;
default:
return -1;
}
return 0;
}
static int StrokeAttribute_mathutils_set(blender::BaseMathObject *bmo, int subtype)
{
BPy_StrokeAttribute *self = (BPy_StrokeAttribute *)bmo->cb_user;
switch (subtype) {
case MATHUTILS_SUBTYPE_COLOR:
self->sa->setColor(bmo->data[0], bmo->data[1], bmo->data[2]);
break;
case MATHUTILS_SUBTYPE_THICKNESS:
self->sa->setThickness(bmo->data[0], bmo->data[1]);
break;
default:
return -1;
}
return 0;
}
static int StrokeAttribute_mathutils_get_index(blender::BaseMathObject *bmo,
int subtype,
int index)
{
BPy_StrokeAttribute *self = (BPy_StrokeAttribute *)bmo->cb_user;
switch (subtype) {
case MATHUTILS_SUBTYPE_COLOR:
switch (index) {
case 0:
bmo->data[0] = self->sa->getColorR();
break;
case 1:
bmo->data[1] = self->sa->getColorG();
break;
case 2:
bmo->data[2] = self->sa->getColorB();
break;
default:
return -1;
}
break;
case MATHUTILS_SUBTYPE_THICKNESS:
switch (index) {
case 0:
bmo->data[0] = self->sa->getThicknessR();
break;
case 1:
bmo->data[1] = self->sa->getThicknessL();
break;
default:
return -1;
}
break;
default:
return -1;
}
return 0;
}
static int StrokeAttribute_mathutils_set_index(blender::BaseMathObject *bmo,
int subtype,
int index)
{
BPy_StrokeAttribute *self = (BPy_StrokeAttribute *)bmo->cb_user;
switch (subtype) {
case MATHUTILS_SUBTYPE_COLOR: {
float r = (index == 0) ? bmo->data[0] : self->sa->getColorR();
float g = (index == 1) ? bmo->data[1] : self->sa->getColorG();
float b = (index == 2) ? bmo->data[2] : self->sa->getColorB();
self->sa->setColor(r, g, b);
break;
}
case MATHUTILS_SUBTYPE_THICKNESS: {
float tr = (index == 0) ? bmo->data[0] : self->sa->getThicknessR();
float tl = (index == 1) ? bmo->data[1] : self->sa->getThicknessL();
self->sa->setThickness(tr, tl);
break;
}
default:
return -1;
}
return 0;
}
static blender::Mathutils_Callback StrokeAttribute_mathutils_cb = {
StrokeAttribute_mathutils_check,
StrokeAttribute_mathutils_get,
StrokeAttribute_mathutils_set,
StrokeAttribute_mathutils_get_index,
StrokeAttribute_mathutils_set_index,
};
static uchar StrokeAttribute_mathutils_cb_index = -1;
void StrokeAttribute_mathutils_register_callback()
{
StrokeAttribute_mathutils_cb_index = Mathutils_RegisterCallback(&StrokeAttribute_mathutils_cb);
}
/*----------------------StrokeAttribute get/setters ----------------------------*/
PyDoc_STRVAR(
/* Wrap. */
StrokeAttribute_alpha_doc,
"Alpha component of the stroke color.\n"
"\n"
":type: float\n");
static PyObject *StrokeAttribute_alpha_get(BPy_StrokeAttribute *self, void * /*closure*/)
{
return PyFloat_FromDouble(self->sa->getAlpha());
}
static int StrokeAttribute_alpha_set(BPy_StrokeAttribute *self,
PyObject *value,
void * /*closure*/)
{
float scalar;
if ((scalar = PyFloat_AsDouble(value)) == -1.0f && PyErr_Occurred()) {
/* parsed item not a number */
PyErr_SetString(PyExc_TypeError, "value must be a number");
return -1;
}
self->sa->setAlpha(scalar);
return 0;
}
PyDoc_STRVAR(
/* Wrap. */
StrokeAttribute_color_doc,
"RGB components of the stroke color.\n"
"\n"
":type: :class:`mathutils.Color`\n");
static PyObject *StrokeAttribute_color_get(BPy_StrokeAttribute *self, void * /*closure*/)
{
return blender::Color_CreatePyObject_cb(
(PyObject *)self, StrokeAttribute_mathutils_cb_index, MATHUTILS_SUBTYPE_COLOR);
}
static int StrokeAttribute_color_set(BPy_StrokeAttribute *self,
PyObject *value,
void * /*closure*/)
{
float v[3];
if (blender::mathutils_array_parse(v, 3, 3, value, "value must be a 3-dimensional vector") == -1)
{
return -1;
}
self->sa->setColor(v[0], v[1], v[2]);
return 0;
}
PyDoc_STRVAR(
/* Wrap. */
StrokeAttribute_thickness_doc,
"Right and left components of the stroke thickness.\n"
"The right (left) component is the thickness on the right (left) of the vertex\n"
"when following the stroke.\n"
"\n"
":type: :class:`mathutils.Vector`\n");
static PyObject *StrokeAttribute_thickness_get(BPy_StrokeAttribute *self, void * /*closure*/)
{
return blender::Vector_CreatePyObject_cb(
(PyObject *)self, 2, StrokeAttribute_mathutils_cb_index, MATHUTILS_SUBTYPE_THICKNESS);
}
static int StrokeAttribute_thickness_set(BPy_StrokeAttribute *self,
PyObject *value,
void * /*closure*/)
{
float v[2];
if (blender::mathutils_array_parse(v, 2, 2, value, "value must be a 2-dimensional vector") == -1)
{
return -1;
}
self->sa->setThickness(v[0], v[1]);
return 0;
}
PyDoc_STRVAR(
/* Wrap. */
StrokeAttribute_visible_doc,
"The visibility flag. True if the StrokeVertex is visible.\n"
"\n"
":type: bool\n");
static PyObject *StrokeAttribute_visible_get(BPy_StrokeAttribute *self, void * /*closure*/)
{
return PyBool_from_bool(self->sa->isVisible());
}
static int StrokeAttribute_visible_set(BPy_StrokeAttribute *self,
PyObject *value,
void * /*closure*/)
{
if (!PyBool_Check(value)) {
PyErr_SetString(PyExc_TypeError, "value must be boolean");
return -1;
}
self->sa->setVisible(bool_from_PyBool(value));
return 0;
}
static PyGetSetDef BPy_StrokeAttribute_getseters[] = {
{"alpha",
(getter)StrokeAttribute_alpha_get,
(setter)StrokeAttribute_alpha_set,
StrokeAttribute_alpha_doc,
nullptr},
{"color",
(getter)StrokeAttribute_color_get,
(setter)StrokeAttribute_color_set,
StrokeAttribute_color_doc,
nullptr},
{"thickness",
(getter)StrokeAttribute_thickness_get,
(setter)StrokeAttribute_thickness_set,
StrokeAttribute_thickness_doc,
nullptr},
{"visible",
(getter)StrokeAttribute_visible_get,
(setter)StrokeAttribute_visible_set,
StrokeAttribute_visible_doc,
nullptr},
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
};
/*-----------------------BPy_StrokeAttribute type definition ------------------------------*/
PyTypeObject StrokeAttribute_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "StrokeAttribute",
/*tp_basicsize*/ sizeof(BPy_StrokeAttribute),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ (destructor)StrokeAttribute_dealloc,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ (reprfunc)StrokeAttribute_repr,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ nullptr,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
/*tp_doc*/ StrokeAttribute_doc,
/*tp_traverse*/ nullptr,
/*tp_clear*/ nullptr,
/*tp_richcompare*/ nullptr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ BPy_StrokeAttribute_methods,
/*tp_members*/ nullptr,
/*tp_getset*/ BPy_StrokeAttribute_getseters,
/*tp_base*/ nullptr,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ (initproc)StrokeAttribute_init,
/*tp_alloc*/ nullptr,
/*tp_new*/ PyType_GenericNew,
};
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,36 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
extern "C" {
#include <Python.h>
}
#include "../stroke/Stroke.h"
///////////////////////////////////////////////////////////////////////////////////////////
extern PyTypeObject StrokeAttribute_Type;
#define BPy_StrokeAttribute_Check(v) \
(PyObject_IsInstance((PyObject *)v, (PyObject *)&StrokeAttribute_Type))
/*---------------------------Python BPy_StrokeAttribute structure definition----------*/
struct BPy_StrokeAttribute {
PyObject_HEAD
Freestyle::StrokeAttribute *sa;
bool borrowed; /* true if *sa is a borrowed reference */
};
/*---------------------------Python BPy_StrokeAttribute visible prototypes-----------*/
int StrokeAttribute_Init(PyObject *module);
void StrokeAttribute_mathutils_register_callback();
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,313 @@
/* SPDX-FileCopyrightText: 2004-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_StrokeShader.h"
#include "BPy_Convert.h"
#include "Interface1D/BPy_Stroke.h"
#include "StrokeShader/BPy_BackboneStretcherShader.h"
#include "StrokeShader/BPy_BezierCurveShader.h"
#include "StrokeShader/BPy_BlenderTextureShader.h"
#include "StrokeShader/BPy_CalligraphicShader.h"
#include "StrokeShader/BPy_ColorNoiseShader.h"
#include "StrokeShader/BPy_ConstantColorShader.h"
#include "StrokeShader/BPy_ConstantThicknessShader.h"
#include "StrokeShader/BPy_ConstrainedIncreasingThicknessShader.h"
#include "StrokeShader/BPy_GuidingLinesShader.h"
#include "StrokeShader/BPy_IncreasingColorShader.h"
#include "StrokeShader/BPy_IncreasingThicknessShader.h"
#include "StrokeShader/BPy_PolygonalizationShader.h"
#include "StrokeShader/BPy_SamplingShader.h"
#include "StrokeShader/BPy_SmoothingShader.h"
#include "StrokeShader/BPy_SpatialNoiseShader.h"
#include "StrokeShader/BPy_StrokeTextureStepShader.h"
#include "StrokeShader/BPy_ThicknessNoiseShader.h"
#include "StrokeShader/BPy_TipRemoverShader.h"
using namespace Freestyle;
///////////////////////////////////////////////////////////////////////////////////////////
//-------------------MODULE INITIALIZATION--------------------------------
int StrokeShader_Init(PyObject *module)
{
if (module == nullptr) {
return -1;
}
if (PyType_Ready(&StrokeShader_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "StrokeShader", (PyObject *)&StrokeShader_Type);
if (PyType_Ready(&BackboneStretcherShader_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(
module, "BackboneStretcherShader", (PyObject *)&BackboneStretcherShader_Type);
if (PyType_Ready(&BezierCurveShader_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "BezierCurveShader", (PyObject *)&BezierCurveShader_Type);
if (PyType_Ready(&BlenderTextureShader_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "BlenderTextureShader", (PyObject *)&BlenderTextureShader_Type);
if (PyType_Ready(&CalligraphicShader_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "CalligraphicShader", (PyObject *)&CalligraphicShader_Type);
if (PyType_Ready(&ColorNoiseShader_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "ColorNoiseShader", (PyObject *)&ColorNoiseShader_Type);
if (PyType_Ready(&ConstantColorShader_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "ConstantColorShader", (PyObject *)&ConstantColorShader_Type);
if (PyType_Ready(&ConstantThicknessShader_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(
module, "ConstantThicknessShader", (PyObject *)&ConstantThicknessShader_Type);
if (PyType_Ready(&ConstrainedIncreasingThicknessShader_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module,
"ConstrainedIncreasingThicknessShader",
(PyObject *)&ConstrainedIncreasingThicknessShader_Type);
if (PyType_Ready(&GuidingLinesShader_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "GuidingLinesShader", (PyObject *)&GuidingLinesShader_Type);
if (PyType_Ready(&IncreasingColorShader_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "IncreasingColorShader", (PyObject *)&IncreasingColorShader_Type);
if (PyType_Ready(&IncreasingThicknessShader_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(
module, "IncreasingThicknessShader", (PyObject *)&IncreasingThicknessShader_Type);
if (PyType_Ready(&PolygonalizationShader_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(
module, "PolygonalizationShader", (PyObject *)&PolygonalizationShader_Type);
if (PyType_Ready(&SamplingShader_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "SamplingShader", (PyObject *)&SamplingShader_Type);
if (PyType_Ready(&SmoothingShader_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "SmoothingShader", (PyObject *)&SmoothingShader_Type);
if (PyType_Ready(&SpatialNoiseShader_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "SpatialNoiseShader", (PyObject *)&SpatialNoiseShader_Type);
if (PyType_Ready(&StrokeTextureStepShader_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(
module, "StrokeTextureStepShader", (PyObject *)&StrokeTextureStepShader_Type);
if (PyType_Ready(&ThicknessNoiseShader_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "ThicknessNoiseShader", (PyObject *)&ThicknessNoiseShader_Type);
if (PyType_Ready(&TipRemoverShader_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "TipRemoverShader", (PyObject *)&TipRemoverShader_Type);
return 0;
}
//------------------------INSTANCE METHODS ----------------------------------
PyDoc_STRVAR(
/* Wrap. */
StrokeShader___doc__,
"Base class for stroke shaders. Any stroke shader must inherit from\n"
"this class and overload the shade() method. A StrokeShader is\n"
"designed to modify stroke attributes such as thickness, color,\n"
"geometry, texture, blending mode, and so on. The basic way for this\n"
"operation is to iterate over the stroke vertices of the :class:`Stroke`\n"
"and to modify the :class:`StrokeAttribute` of each vertex. Here is a\n"
"code example of such an iteration::\n"
"\n"
" it = ioStroke.strokeVerticesBegin()\n"
" while not it.is_end:\n"
" att = it.object.attribute\n"
" ## perform here any attribute modification\n"
" it.increment()\n"
"\n"
".. method:: __init__()\n"
"\n"
" Default constructor.\n");
static int StrokeShader___init__(BPy_StrokeShader *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {nullptr};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "", (char **)kwlist)) {
return -1;
}
self->ss = new StrokeShader();
self->ss->py_ss = (PyObject *)self;
return 0;
}
static void StrokeShader___dealloc__(BPy_StrokeShader *self)
{
delete self->ss;
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *StrokeShader___repr__(BPy_StrokeShader *self)
{
return PyUnicode_FromFormat("type: %s - address: %p", Py_TYPE(self)->tp_name, self->ss);
}
PyDoc_STRVAR(
/* Wrap. */
StrokeShader_shade___doc__,
".. method:: shade(stroke)\n"
"\n"
" The shading method. Must be overloaded by inherited classes.\n"
"\n"
" :param stroke: A Stroke object.\n"
" :type stroke: :class:`Stroke`\n");
static PyObject *StrokeShader_shade(BPy_StrokeShader *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"stroke", nullptr};
PyObject *py_s = nullptr;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist, &Stroke_Type, &py_s)) {
return nullptr;
}
if (typeid(*(self->ss)) == typeid(StrokeShader)) {
PyErr_SetString(PyExc_TypeError, "shade method not properly overridden");
return nullptr;
}
if (self->ss->shade(*(((BPy_Stroke *)py_s)->s)) < 0) {
if (!PyErr_Occurred()) {
string class_name(Py_TYPE(self)->tp_name);
PyErr_SetString(PyExc_RuntimeError, (class_name + " shade method failed").c_str());
}
return nullptr;
}
Py_RETURN_NONE;
}
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wcast-function-type"
# else
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
# endif
#endif
static PyMethodDef BPy_StrokeShader_methods[] = {
{"shade",
(PyCFunction)StrokeShader_shade,
METH_VARARGS | METH_KEYWORDS,
StrokeShader_shade___doc__},
{nullptr, nullptr, 0, nullptr},
};
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic pop
# else
# pragma GCC diagnostic pop
# endif
#endif
/*----------------------StrokeShader get/setters ----------------------------*/
PyDoc_STRVAR(
/* Wrap. */
StrokeShader_name_doc,
"The name of the stroke shader.\n"
"\n"
":type: str\n");
static PyObject *StrokeShader_name_get(BPy_StrokeShader *self, void * /*closure*/)
{
return PyUnicode_FromString(Py_TYPE(self)->tp_name);
}
static PyGetSetDef BPy_StrokeShader_getseters[] = {
{"name", (getter)StrokeShader_name_get, (setter) nullptr, StrokeShader_name_doc, nullptr},
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
};
/*-----------------------BPy_StrokeShader type definition ------------------------------*/
PyTypeObject StrokeShader_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "StrokeShader",
/*tp_basicsize*/ sizeof(BPy_StrokeShader),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ (destructor)StrokeShader___dealloc__,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ (reprfunc)StrokeShader___repr__,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ nullptr,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
/*tp_doc*/ StrokeShader___doc__,
/*tp_traverse*/ nullptr,
/*tp_clear*/ nullptr,
/*tp_richcompare*/ nullptr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ BPy_StrokeShader_methods,
/*tp_members*/ nullptr,
/*tp_getset*/ BPy_StrokeShader_getseters,
/*tp_base*/ nullptr,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ (initproc)StrokeShader___init__,
/*tp_alloc*/ nullptr,
/*tp_new*/ PyType_GenericNew,
};
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,36 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
extern "C" {
#include <Python.h>
}
#include "../system/FreestyleConfig.h"
#include "../stroke/StrokeShader.h"
///////////////////////////////////////////////////////////////////////////////////////////
extern PyTypeObject StrokeShader_Type;
#define BPy_StrokeShader_Check(v) \
(PyObject_IsInstance((PyObject *)v, (PyObject *)&StrokeShader_Type))
/*---------------------------Python BPy_StrokeShader structure definition----------*/
struct BPy_StrokeShader {
PyObject_HEAD
Freestyle::StrokeShader *ss;
};
/*---------------------------Python BPy_StrokeShader visible prototypes-----------*/
int StrokeShader_Init(PyObject *module);
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,150 @@
/* SPDX-FileCopyrightText: 2004-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_UnaryFunction0D.h"
#include "UnaryFunction0D/BPy_UnaryFunction0DDouble.h"
#include "UnaryFunction0D/BPy_UnaryFunction0DEdgeNature.h"
#include "UnaryFunction0D/BPy_UnaryFunction0DFloat.h"
#include "UnaryFunction0D/BPy_UnaryFunction0DId.h"
#include "UnaryFunction0D/BPy_UnaryFunction0DMaterial.h"
#include "UnaryFunction0D/BPy_UnaryFunction0DUnsigned.h"
#include "UnaryFunction0D/BPy_UnaryFunction0DVec2f.h"
#include "UnaryFunction0D/BPy_UnaryFunction0DVec3f.h"
#include "UnaryFunction0D/BPy_UnaryFunction0DVectorViewShape.h"
#include "UnaryFunction0D/BPy_UnaryFunction0DViewShape.h"
using namespace Freestyle;
///////////////////////////////////////////////////////////////////////////////////////////
//-------------------MODULE INITIALIZATION--------------------------------
int UnaryFunction0D_Init(PyObject *module)
{
if (module == nullptr) {
return -1;
}
if (PyType_Ready(&UnaryFunction0D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "UnaryFunction0D", (PyObject *)&UnaryFunction0D_Type);
UnaryFunction0DDouble_Init(module);
UnaryFunction0DEdgeNature_Init(module);
UnaryFunction0DFloat_Init(module);
UnaryFunction0DId_Init(module);
UnaryFunction0DMaterial_Init(module);
UnaryFunction0DUnsigned_Init(module);
UnaryFunction0DVec2f_Init(module);
UnaryFunction0DVec3f_Init(module);
UnaryFunction0DVectorViewShape_Init(module);
UnaryFunction0DViewShape_Init(module);
return 0;
}
//------------------------INSTANCE METHODS ----------------------------------
PyDoc_STRVAR(
/* Wrap. */
UnaryFunction0D___doc__,
"Base class for Unary Functions (functors) working on\n"
":class:`Interface0DIterator`. A unary function will be used by\n"
"invoking __call__() on an Interface0DIterator. In Python, several\n"
"different subclasses of UnaryFunction0D are used depending on the\n"
"types of functors' return values. For example, you would inherit from\n"
"a :class:`UnaryFunction0DDouble` if you wish to define a function that\n"
"returns a double value. Available UnaryFunction0D subclasses are:\n"
"\n"
"* :class:`UnaryFunction0DDouble`\n"
"* :class:`UnaryFunction0DEdgeNature`\n"
"* :class:`UnaryFunction0DFloat`\n"
"* :class:`UnaryFunction0DId`\n"
"* :class:`UnaryFunction0DMaterial`\n"
"* :class:`UnaryFunction0DUnsigned`\n"
"* :class:`UnaryFunction0DVec2f`\n"
"* :class:`UnaryFunction0DVec3f`\n"
"* :class:`UnaryFunction0DVectorViewShape`\n"
"* :class:`UnaryFunction0DViewShape`\n");
static void UnaryFunction0D___dealloc__(BPy_UnaryFunction0D *self)
{
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *UnaryFunction0D___repr__(BPy_UnaryFunction0D * /*self*/)
{
return PyUnicode_FromString("UnaryFunction0D");
}
/*----------------------UnaryFunction0D get/setters ----------------------------*/
PyDoc_STRVAR(
/* Wrap. */
UnaryFunction0D_name_doc,
"The name of the unary 0D function.\n"
"\n"
":type: str\n");
static PyObject *UnaryFunction0D_name_get(BPy_UnaryFunction0D *self, void * /*closure*/)
{
return PyUnicode_FromString(Py_TYPE(self)->tp_name);
}
static PyGetSetDef BPy_UnaryFunction0D_getseters[] = {
{"name",
(getter)UnaryFunction0D_name_get,
(setter) nullptr,
UnaryFunction0D_name_doc,
nullptr},
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
};
/*-----------------------BPy_UnaryFunction0D type definition ------------------------------*/
PyTypeObject UnaryFunction0D_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "UnaryFunction0D",
/*tp_basicsize*/ sizeof(BPy_UnaryFunction0D),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ (destructor)UnaryFunction0D___dealloc__,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ (reprfunc)UnaryFunction0D___repr__,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ nullptr,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
/*tp_doc*/ UnaryFunction0D___doc__,
/*tp_traverse*/ nullptr,
/*tp_clear*/ nullptr,
/*tp_richcompare*/ nullptr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ nullptr,
/*tp_members*/ nullptr,
/*tp_getset*/ BPy_UnaryFunction0D_getseters,
/*tp_base*/ nullptr,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ nullptr,
/*tp_alloc*/ nullptr,
/*tp_new*/ PyType_GenericNew,
};
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,34 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
extern "C" {
#include <Python.h>
}
#include "../view_map/Functions0D.h"
///////////////////////////////////////////////////////////////////////////////////////////
extern PyTypeObject UnaryFunction0D_Type;
#define BPy_UnaryFunction0D_Check(v) \
(PyObject_IsInstance((PyObject *)v, (PyObject *)&UnaryFunction0D_Type))
/*---------------------------Python BPy_UnaryFunction0D structure definition----------*/
struct BPy_UnaryFunction0D {
PyObject_HEAD
PyObject *py_uf0D;
};
/*---------------------------Python BPy_UnaryFunction0D visible prototypes-----------*/
int UnaryFunction0D_Init(PyObject *module);
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,144 @@
/* SPDX-FileCopyrightText: 2004-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_UnaryFunction1D.h"
#include "UnaryFunction1D/BPy_UnaryFunction1DDouble.h"
#include "UnaryFunction1D/BPy_UnaryFunction1DEdgeNature.h"
#include "UnaryFunction1D/BPy_UnaryFunction1DFloat.h"
#include "UnaryFunction1D/BPy_UnaryFunction1DUnsigned.h"
#include "UnaryFunction1D/BPy_UnaryFunction1DVec2f.h"
#include "UnaryFunction1D/BPy_UnaryFunction1DVec3f.h"
#include "UnaryFunction1D/BPy_UnaryFunction1DVectorViewShape.h"
#include "UnaryFunction1D/BPy_UnaryFunction1DVoid.h"
using namespace Freestyle;
///////////////////////////////////////////////////////////////////////////////////////////
//-------------------MODULE INITIALIZATION--------------------------------
int UnaryFunction1D_Init(PyObject *module)
{
if (module == nullptr) {
return -1;
}
if (PyType_Ready(&UnaryFunction1D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "UnaryFunction1D", (PyObject *)&UnaryFunction1D_Type);
UnaryFunction1DDouble_Init(module);
UnaryFunction1DEdgeNature_Init(module);
UnaryFunction1DFloat_Init(module);
UnaryFunction1DUnsigned_Init(module);
UnaryFunction1DVec2f_Init(module);
UnaryFunction1DVec3f_Init(module);
UnaryFunction1DVectorViewShape_Init(module);
UnaryFunction1DVoid_Init(module);
return 0;
}
//------------------------INSTANCE METHODS ----------------------------------
PyDoc_STRVAR(
/* Wrap. */
UnaryFunction1D___doc__,
"Base class for Unary Functions (functors) working on\n"
":class:`Interface1D`. A unary function will be used by invoking\n"
"__call__() on an Interface1D. In Python, several different subclasses\n"
"of UnaryFunction1D are used depending on the types of functors' return\n"
"values. For example, you would inherit from a\n"
":class:`UnaryFunction1DDouble` if you wish to define a function that\n"
"returns a double value. Available UnaryFunction1D subclasses are:\n"
"\n"
"* :class:`UnaryFunction1DDouble`\n"
"* :class:`UnaryFunction1DEdgeNature`\n"
"* :class:`UnaryFunction1DFloat`\n"
"* :class:`UnaryFunction1DUnsigned`\n"
"* :class:`UnaryFunction1DVec2f`\n"
"* :class:`UnaryFunction1DVec3f`\n"
"* :class:`UnaryFunction1DVectorViewShape`\n"
"* :class:`UnaryFunction1DVoid`\n");
static void UnaryFunction1D___dealloc__(BPy_UnaryFunction1D *self)
{
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *UnaryFunction1D___repr__(BPy_UnaryFunction1D * /*self*/)
{
return PyUnicode_FromString("UnaryFunction1D");
}
/*----------------------UnaryFunction1D get/setters ----------------------------*/
PyDoc_STRVAR(
/* Wrap. */
UnaryFunction1D_name_doc,
"The name of the unary 1D function.\n"
"\n"
":type: str\n");
static PyObject *UnaryFunction1D_name_get(BPy_UnaryFunction1D *self, void * /*closure*/)
{
return PyUnicode_FromString(Py_TYPE(self)->tp_name);
}
static PyGetSetDef BPy_UnaryFunction1D_getseters[] = {
{"name",
(getter)UnaryFunction1D_name_get,
(setter) nullptr,
UnaryFunction1D_name_doc,
nullptr},
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
};
/*-----------------------BPy_UnaryFunction1D type definition ------------------------------*/
PyTypeObject UnaryFunction1D_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "UnaryFunction1D",
/*tp_basicsize*/ sizeof(BPy_UnaryFunction1D),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ (destructor)UnaryFunction1D___dealloc__,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ (reprfunc)UnaryFunction1D___repr__,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ nullptr,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
/*tp_doc*/ UnaryFunction1D___doc__,
/*tp_traverse*/ nullptr,
/*tp_clear*/ nullptr,
/*tp_richcompare*/ nullptr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ nullptr,
/*tp_members*/ nullptr,
/*tp_getset*/ BPy_UnaryFunction1D_getseters,
/*tp_base*/ nullptr,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ nullptr,
/*tp_alloc*/ nullptr,
/*tp_new*/ PyType_GenericNew,
};
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,34 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
extern "C" {
#include <Python.h>
}
#include "../view_map/Functions1D.h"
///////////////////////////////////////////////////////////////////////////////////////////
extern PyTypeObject UnaryFunction1D_Type;
#define BPy_UnaryFunction1D_Check(v) \
(PyObject_IsInstance((PyObject *)v, (PyObject *)&UnaryFunction1D_Type))
/*---------------------------Python BPy_UnaryFunction1D structure definition----------*/
struct BPy_UnaryFunction1D {
PyObject_HEAD
PyObject *py_uf1D;
};
/*---------------------------Python BPy_UnaryFunction1D visible prototypes-----------*/
int UnaryFunction1D_Init(PyObject *module);
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,192 @@
/* SPDX-FileCopyrightText: 2004-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_UnaryPredicate0D.h"
#include "BPy_Convert.h"
#include "Iterator/BPy_Interface0DIterator.h"
#include "UnaryPredicate0D/BPy_FalseUP0D.h"
#include "UnaryPredicate0D/BPy_TrueUP0D.h"
using namespace Freestyle;
///////////////////////////////////////////////////////////////////////////////////////////
//-------------------MODULE INITIALIZATION--------------------------------
int UnaryPredicate0D_Init(PyObject *module)
{
if (module == nullptr) {
return -1;
}
if (PyType_Ready(&UnaryPredicate0D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "UnaryPredicate0D", (PyObject *)&UnaryPredicate0D_Type);
if (PyType_Ready(&FalseUP0D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "FalseUP0D", (PyObject *)&FalseUP0D_Type);
if (PyType_Ready(&TrueUP0D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "TrueUP0D", (PyObject *)&TrueUP0D_Type);
return 0;
}
//------------------------INSTANCE METHODS ----------------------------------
PyDoc_STRVAR(
/* Wrap. */
UnaryPredicate0D___doc__,
"Base class for unary predicates that work on\n"
":class:`Interface0DIterator`. A UnaryPredicate0D is a functor that\n"
"evaluates a condition on an Interface0DIterator and returns true or\n"
"false depending on whether this condition is satisfied or not. The\n"
"UnaryPredicate0D is used by invoking its __call__() method. Any\n"
"inherited class must overload the __call__() method.\n"
"\n"
".. method:: __init__()\n"
"\n"
" Default constructor.\n"
"\n"
".. method:: __call__(it)\n"
"\n"
" Must be overload by inherited classes.\n"
"\n"
" :param it: The Interface0DIterator pointing onto the Interface0D at\n"
" which we wish to evaluate the predicate.\n"
" :type it: :class:`Interface0DIterator`\n"
" :return: True if the condition is satisfied, false otherwise.\n"
" :rtype: bool\n");
static int UnaryPredicate0D___init__(BPy_UnaryPredicate0D *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {nullptr};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "", (char **)kwlist)) {
return -1;
}
self->up0D = new UnaryPredicate0D();
self->up0D->py_up0D = (PyObject *)self;
return 0;
}
static void UnaryPredicate0D___dealloc__(BPy_UnaryPredicate0D *self)
{
delete self->up0D;
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *UnaryPredicate0D___repr__(BPy_UnaryPredicate0D *self)
{
return PyUnicode_FromFormat("type: %s - address: %p", Py_TYPE(self)->tp_name, self->up0D);
}
static PyObject *UnaryPredicate0D___call__(BPy_UnaryPredicate0D *self,
PyObject *args,
PyObject *kwds)
{
static const char *kwlist[] = {"it", nullptr};
PyObject *py_if0D_it;
if (!PyArg_ParseTupleAndKeywords(
args, kwds, "O!", (char **)kwlist, &Interface0DIterator_Type, &py_if0D_it))
{
return nullptr;
}
Interface0DIterator *if0D_it = ((BPy_Interface0DIterator *)py_if0D_it)->if0D_it;
if (!if0D_it) {
string class_name(Py_TYPE(self)->tp_name);
PyErr_SetString(PyExc_RuntimeError, (class_name + " has no Interface0DIterator").c_str());
return nullptr;
}
if (typeid(*(self->up0D)) == typeid(UnaryPredicate0D)) {
PyErr_SetString(PyExc_TypeError, "__call__ method not properly overridden");
return nullptr;
}
if (self->up0D->operator()(*if0D_it) < 0) {
if (!PyErr_Occurred()) {
string class_name(Py_TYPE(self)->tp_name);
PyErr_SetString(PyExc_RuntimeError, (class_name + " __call__ method failed").c_str());
}
return nullptr;
}
return PyBool_from_bool(self->up0D->result);
}
/*----------------------UnaryPredicate0D get/setters ----------------------------*/
PyDoc_STRVAR(
/* Wrap. */
UnaryPredicate0D_name_doc,
"The name of the unary 0D predicate.\n"
"\n"
":type: str\n");
static PyObject *UnaryPredicate0D_name_get(BPy_UnaryPredicate0D *self, void * /*closure*/)
{
return PyUnicode_FromString(Py_TYPE(self)->tp_name);
}
static PyGetSetDef BPy_UnaryPredicate0D_getseters[] = {
{"name",
(getter)UnaryPredicate0D_name_get,
(setter) nullptr,
UnaryPredicate0D_name_doc,
nullptr},
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
};
/*-----------------------BPy_UnaryPredicate0D type definition ------------------------------*/
PyTypeObject UnaryPredicate0D_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "UnaryPredicate0D",
/*tp_basicsize*/ sizeof(BPy_UnaryPredicate0D),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ (destructor)UnaryPredicate0D___dealloc__,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ (reprfunc)UnaryPredicate0D___repr__,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ (ternaryfunc)UnaryPredicate0D___call__,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
/*tp_doc*/ UnaryPredicate0D___doc__,
/*tp_traverse*/ nullptr,
/*tp_clear*/ nullptr,
/*tp_richcompare*/ nullptr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ nullptr,
/*tp_members*/ nullptr,
/*tp_getset*/ BPy_UnaryPredicate0D_getseters,
/*tp_base*/ nullptr,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ (initproc)UnaryPredicate0D___init__,
/*tp_alloc*/ nullptr,
/*tp_new*/ PyType_GenericNew,
};
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,34 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
extern "C" {
#include <Python.h>
}
#include "../stroke/Predicates0D.h"
///////////////////////////////////////////////////////////////////////////////////////////
extern PyTypeObject UnaryPredicate0D_Type;
#define BPy_UnaryPredicate0D_Check(v) \
(PyObject_IsInstance((PyObject *)v, (PyObject *)&UnaryPredicate0D_Type))
/*---------------------------Python BPy_UnaryPredicate0D structure definition----------*/
struct BPy_UnaryPredicate0D {
PyObject_HEAD
Freestyle::UnaryPredicate0D *up0D;
};
/*---------------------------Python BPy_UnaryPredicate0D visible prototypes-----------*/
int UnaryPredicate0D_Init(PyObject *module);
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,242 @@
/* SPDX-FileCopyrightText: 2004-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_UnaryPredicate1D.h"
#include "BPy_Convert.h"
#include "BPy_Interface1D.h"
#include "UnaryPredicate1D/BPy_ContourUP1D.h"
#include "UnaryPredicate1D/BPy_DensityLowerThanUP1D.h"
#include "UnaryPredicate1D/BPy_EqualToChainingTimeStampUP1D.h"
#include "UnaryPredicate1D/BPy_EqualToTimeStampUP1D.h"
#include "UnaryPredicate1D/BPy_ExternalContourUP1D.h"
#include "UnaryPredicate1D/BPy_FalseUP1D.h"
#include "UnaryPredicate1D/BPy_QuantitativeInvisibilityUP1D.h"
#include "UnaryPredicate1D/BPy_ShapeUP1D.h"
#include "UnaryPredicate1D/BPy_TrueUP1D.h"
#include "UnaryPredicate1D/BPy_WithinImageBoundaryUP1D.h"
using namespace Freestyle;
///////////////////////////////////////////////////////////////////////////////////////////
//-------------------MODULE INITIALIZATION--------------------------------
int UnaryPredicate1D_Init(PyObject *module)
{
if (module == nullptr) {
return -1;
}
if (PyType_Ready(&UnaryPredicate1D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "UnaryPredicate1D", (PyObject *)&UnaryPredicate1D_Type);
if (PyType_Ready(&ContourUP1D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "ContourUP1D", (PyObject *)&ContourUP1D_Type);
if (PyType_Ready(&DensityLowerThanUP1D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "DensityLowerThanUP1D", (PyObject *)&DensityLowerThanUP1D_Type);
if (PyType_Ready(&EqualToChainingTimeStampUP1D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(
module, "EqualToChainingTimeStampUP1D", (PyObject *)&EqualToChainingTimeStampUP1D_Type);
if (PyType_Ready(&EqualToTimeStampUP1D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "EqualToTimeStampUP1D", (PyObject *)&EqualToTimeStampUP1D_Type);
if (PyType_Ready(&ExternalContourUP1D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "ExternalContourUP1D", (PyObject *)&ExternalContourUP1D_Type);
if (PyType_Ready(&FalseUP1D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "FalseUP1D", (PyObject *)&FalseUP1D_Type);
if (PyType_Ready(&QuantitativeInvisibilityUP1D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(
module, "QuantitativeInvisibilityUP1D", (PyObject *)&QuantitativeInvisibilityUP1D_Type);
if (PyType_Ready(&ShapeUP1D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "ShapeUP1D", (PyObject *)&ShapeUP1D_Type);
if (PyType_Ready(&TrueUP1D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "TrueUP1D", (PyObject *)&TrueUP1D_Type);
if (PyType_Ready(&WithinImageBoundaryUP1D_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(
module, "WithinImageBoundaryUP1D", (PyObject *)&WithinImageBoundaryUP1D_Type);
return 0;
}
//------------------------INSTANCE METHODS ----------------------------------
PyDoc_STRVAR(
/* Wrap. */
UnaryPredicate1D___doc__,
"Base class for unary predicates that work on :class:`Interface1D`. A\n"
"UnaryPredicate1D is a functor that evaluates a condition on a\n"
"Interface1D and returns true or false depending on whether this\n"
"condition is satisfied or not. The UnaryPredicate1D is used by\n"
"invoking its __call__() method. Any inherited class must overload the\n"
"__call__() method.\n"
"\n"
".. method:: __init__()\n"
"\n"
" Default constructor.\n"
"\n"
".. method:: __call__(inter)\n"
"\n"
" Must be overload by inherited classes.\n"
"\n"
" :param inter: The Interface1D on which we wish to evaluate the predicate.\n"
" :type inter: :class:`Interface1D`\n"
" :return: True if the condition is satisfied, false otherwise.\n"
" :rtype: bool\n");
static int UnaryPredicate1D___init__(BPy_UnaryPredicate1D *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {nullptr};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "", (char **)kwlist)) {
return -1;
}
self->up1D = new UnaryPredicate1D();
self->up1D->py_up1D = (PyObject *)self;
return 0;
}
static void UnaryPredicate1D___dealloc__(BPy_UnaryPredicate1D *self)
{
delete self->up1D;
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *UnaryPredicate1D___repr__(BPy_UnaryPredicate1D *self)
{
return PyUnicode_FromFormat("type: %s - address: %p", Py_TYPE(self)->tp_name, self->up1D);
}
static PyObject *UnaryPredicate1D___call__(BPy_UnaryPredicate1D *self,
PyObject *args,
PyObject *kwds)
{
static const char *kwlist[] = {"inter", nullptr};
PyObject *py_if1D;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist, &Interface1D_Type, &py_if1D))
{
return nullptr;
}
Interface1D *if1D = ((BPy_Interface1D *)py_if1D)->if1D;
if (!if1D) {
string class_name(Py_TYPE(self)->tp_name);
PyErr_SetString(PyExc_RuntimeError, (class_name + " has no Interface1D").c_str());
return nullptr;
}
if (typeid(*(self->up1D)) == typeid(UnaryPredicate1D)) {
PyErr_SetString(PyExc_TypeError, "__call__ method not properly overridden");
return nullptr;
}
if (self->up1D->operator()(*if1D) < 0) {
if (!PyErr_Occurred()) {
string class_name(Py_TYPE(self)->tp_name);
PyErr_SetString(PyExc_RuntimeError, (class_name + " __call__ method failed").c_str());
}
return nullptr;
}
return PyBool_from_bool(self->up1D->result);
}
/*----------------------UnaryPredicate1D get/setters ----------------------------*/
PyDoc_STRVAR(
/* Wrap. */
UnaryPredicate1D_name_doc,
"The name of the unary 1D predicate.\n"
"\n"
":type: str\n");
static PyObject *UnaryPredicate1D_name_get(BPy_UnaryPredicate1D *self, void * /*closure*/)
{
return PyUnicode_FromString(Py_TYPE(self)->tp_name);
}
static PyGetSetDef BPy_UnaryPredicate1D_getseters[] = {
{"name",
(getter)UnaryPredicate1D_name_get,
(setter) nullptr,
UnaryPredicate1D_name_doc,
nullptr},
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
};
/*-----------------------BPy_UnaryPredicate1D type definition ------------------------------*/
PyTypeObject UnaryPredicate1D_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "UnaryPredicate1D",
/*tp_basicsize*/ sizeof(BPy_UnaryPredicate1D),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ (destructor)UnaryPredicate1D___dealloc__,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ (reprfunc)UnaryPredicate1D___repr__,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ (ternaryfunc)UnaryPredicate1D___call__,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
/*tp_doc*/ UnaryPredicate1D___doc__,
/*tp_traverse*/ nullptr,
/*tp_clear*/ nullptr,
/*tp_richcompare*/ nullptr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ nullptr,
/*tp_members*/ nullptr,
/*tp_getset*/ BPy_UnaryPredicate1D_getseters,
/*tp_base*/ nullptr,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ (initproc)UnaryPredicate1D___init__,
/*tp_alloc*/ nullptr,
/*tp_new*/ PyType_GenericNew,
};
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,34 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
extern "C" {
#include <Python.h>
}
#include "../stroke/Predicates1D.h"
///////////////////////////////////////////////////////////////////////////////////////////
extern PyTypeObject UnaryPredicate1D_Type;
#define BPy_UnaryPredicate1D_Check(v) \
(PyObject_IsInstance((PyObject *)v, (PyObject *)&UnaryPredicate1D_Type))
/*---------------------------Python BPy_UnaryPredicate1D structure definition----------*/
struct BPy_UnaryPredicate1D {
PyObject_HEAD
Freestyle::UnaryPredicate1D *up1D;
};
/*---------------------------Python BPy_UnaryPredicate1D visible prototypes-----------*/
int UnaryPredicate1D_Init(PyObject *module);
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,230 @@
/* SPDX-FileCopyrightText: 2004-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_ViewMap.h"
#include "BPy_BBox.h"
#include "BPy_Convert.h"
#include "Interface1D/BPy_FEdge.h"
#include "Interface1D/BPy_ViewEdge.h"
using namespace Freestyle;
///////////////////////////////////////////////////////////////////////////////////////////
//-------------------MODULE INITIALIZATION--------------------------------
int ViewMap_Init(PyObject *module)
{
if (module == nullptr) {
return -1;
}
if (PyType_Ready(&ViewMap_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "ViewMap", (PyObject *)&ViewMap_Type);
return 0;
}
/*----------------------ViewMap methods----------------------------*/
PyDoc_STRVAR(
/* Wrap. */
ViewMap_doc,
"Class defining the ViewMap.\n"
"\n"
".. method:: __init__()\n"
"\n"
" Default constructor.\n");
static int ViewMap_init(BPy_ViewMap *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {nullptr};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "", (char **)kwlist)) {
return -1;
}
self->vm = new ViewMap();
return 0;
}
static void ViewMap_dealloc(BPy_ViewMap *self)
{
delete self->vm;
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *ViewMap_repr(BPy_ViewMap *self)
{
return PyUnicode_FromFormat("ViewMap - address: %p", self->vm);
}
PyDoc_STRVAR(
/* Wrap. */
ViewMap_get_closest_viewedge_doc,
".. method:: get_closest_viewedge(x, y)\n"
"\n"
" Gets the ViewEdge nearest to the 2D point specified as arguments.\n"
"\n"
" :param x: X coordinate of a 2D point.\n"
" :type x: float\n"
" :param y: Y coordinate of a 2D point.\n"
" :type y: float\n"
" :return: The ViewEdge nearest to the specified 2D point.\n"
" :rtype: :class:`ViewEdge`\n");
static PyObject *ViewMap_get_closest_viewedge(BPy_ViewMap *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"x", "y", nullptr};
double x, y;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "dd", (char **)kwlist, &x, &y)) {
return nullptr;
}
ViewEdge *ve = const_cast<ViewEdge *>(self->vm->getClosestViewEdge(x, y));
if (ve) {
return BPy_ViewEdge_from_ViewEdge(*ve);
}
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
ViewMap_get_closest_fedge_doc,
".. method:: get_closest_fedge(x, y)\n"
"\n"
" Gets the FEdge nearest to the 2D point specified as arguments.\n"
"\n"
" :param x: X coordinate of a 2D point.\n"
" :type x: float\n"
" :param y: Y coordinate of a 2D point.\n"
" :type y: float\n"
" :return: The FEdge nearest to the specified 2D point.\n"
" :rtype: :class:`FEdge`\n");
static PyObject *ViewMap_get_closest_fedge(BPy_ViewMap *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"x", "y", nullptr};
double x, y;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "dd", (char **)kwlist, &x, &y)) {
return nullptr;
}
FEdge *fe = const_cast<FEdge *>(self->vm->getClosestFEdge(x, y));
if (fe) {
return Any_BPy_FEdge_from_FEdge(*fe);
}
Py_RETURN_NONE;
}
// static ViewMap *getInstance ();
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wcast-function-type"
# else
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
# endif
#endif
static PyMethodDef BPy_ViewMap_methods[] = {
{"get_closest_viewedge",
(PyCFunction)ViewMap_get_closest_viewedge,
METH_VARARGS | METH_KEYWORDS,
ViewMap_get_closest_viewedge_doc},
{"get_closest_fedge",
(PyCFunction)ViewMap_get_closest_fedge,
METH_VARARGS | METH_KEYWORDS,
ViewMap_get_closest_fedge_doc},
{nullptr, nullptr, 0, nullptr},
};
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic pop
# else
# pragma GCC diagnostic pop
# endif
#endif
/*----------------------ViewMap get/setters ----------------------------*/
PyDoc_STRVAR(
/* Wrap. */
ViewMap_scene_bbox_doc,
"The 3D bounding box of the scene.\n"
"\n"
":type: :class:`BBox`\n");
static PyObject *ViewMap_scene_bbox_get(BPy_ViewMap *self, void * /*closure*/)
{
return BPy_BBox_from_BBox(self->vm->getScene3dBBox());
}
static int ViewMap_scene_bbox_set(BPy_ViewMap *self, PyObject *value, void * /*closure*/)
{
if (!BPy_BBox_Check(value)) {
PyErr_SetString(PyExc_TypeError, "value must be a BBox");
return -1;
}
self->vm->setScene3dBBox(*(((BPy_BBox *)value)->bb));
return 0;
}
static PyGetSetDef BPy_ViewMap_getseters[] = {
{"scene_bbox",
(getter)ViewMap_scene_bbox_get,
(setter)ViewMap_scene_bbox_set,
ViewMap_scene_bbox_doc,
nullptr},
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
};
/*-----------------------BPy_ViewMap type definition ------------------------------*/
PyTypeObject ViewMap_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "ViewMap",
/*tp_basicsize*/ sizeof(BPy_ViewMap),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ (destructor)ViewMap_dealloc,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ (reprfunc)ViewMap_repr,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ nullptr,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
/*tp_doc*/ ViewMap_doc,
/*tp_traverse*/ nullptr,
/*tp_clear*/ nullptr,
/*tp_richcompare*/ nullptr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ BPy_ViewMap_methods,
/*tp_members*/ nullptr,
/*tp_getset*/ BPy_ViewMap_getseters,
/*tp_base*/ nullptr,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ (initproc)ViewMap_init,
/*tp_alloc*/ nullptr,
/*tp_new*/ PyType_GenericNew,
};
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,33 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
extern "C" {
#include <Python.h>
}
#include "../view_map/ViewMap.h"
///////////////////////////////////////////////////////////////////////////////////////////
extern PyTypeObject ViewMap_Type;
#define BPy_ViewMap_Check(v) (PyObject_IsInstance((PyObject *)v, (PyObject *)&ViewMap_Type))
/*---------------------------Python BPy_ViewMap structure definition----------*/
struct BPy_ViewMap {
PyObject_HEAD
Freestyle::ViewMap *vm;
};
/*---------------------------Python BPy_ViewMap visible prototypes-----------*/
int ViewMap_Init(PyObject *module);
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,413 @@
/* SPDX-FileCopyrightText: 2004-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_ViewShape.h"
#include "BPy_Convert.h"
#include "BPy_SShape.h"
#include "Interface0D/BPy_ViewVertex.h"
#include "Interface1D/BPy_ViewEdge.h"
#include "BLI_sys_types.h"
#include "../generic/py_capi_utils.hh"
using namespace Freestyle;
///////////////////////////////////////////////////////////////////////////////////////////
//-------------------MODULE INITIALIZATION--------------------------------
int ViewShape_Init(PyObject *module)
{
if (module == nullptr) {
return -1;
}
if (PyType_Ready(&ViewShape_Type) < 0) {
return -1;
}
PyModule_AddObjectRef(module, "ViewShape", (PyObject *)&ViewShape_Type);
return 0;
}
/*----------------------ViewShape methods ----------------------------*/
PyDoc_STRVAR(
/* Wrap. */
ViewShape_doc,
"Class gathering the elements of the ViewMap (i.e., :class:`ViewVertex`\n"
"and :class:`ViewEdge`) that are issued from the same input shape.\n"
"\n"
".. method:: __init__(*args)\n"
"\n"
" Accepted call signatures:\n"
"\n"
" - ``__init__()``\n"
" - ``__init__(brother)``\n"
" - ``__init__(sshape)``\n"
"\n"
" Builds a :class:`ViewShape` using the default constructor,\n"
" copy constructor, or from a :class:`SShape`.\n"
"\n"
" :param brother: A ViewShape object.\n"
" :type brother: :class:`ViewShape`\n"
" :param sshape: An SShape object.\n"
" :type sshape: :class:`SShape`\n");
static int ViewShape_init(BPy_ViewShape *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist_1[] = {"brother", nullptr};
static const char *kwlist_2[] = {"sshape", nullptr};
PyObject *obj = nullptr;
if (PyArg_ParseTupleAndKeywords(args, kwds, "|O!", (char **)kwlist_1, &ViewShape_Type, &obj)) {
if (!obj) {
self->vs = new ViewShape();
self->py_ss = nullptr;
}
else {
self->vs = new ViewShape(*(((BPy_ViewShape *)obj)->vs));
self->py_ss = ((BPy_ViewShape *)obj)->py_ss;
}
}
else if ((void)PyErr_Clear(),
PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist_2, &SShape_Type, &obj))
{
BPy_SShape *py_ss = (BPy_SShape *)obj;
self->vs = new ViewShape(py_ss->ss);
self->py_ss = (!py_ss->borrowed) ? py_ss : nullptr;
}
else {
PyErr_SetString(PyExc_TypeError, "invalid argument(s)");
return -1;
}
self->borrowed = false;
Py_XINCREF(self->py_ss);
return 0;
}
static void ViewShape_dealloc(BPy_ViewShape *self)
{
if (self->py_ss) {
self->vs->setSShape((SShape *)nullptr);
Py_DECREF(self->py_ss);
}
if (self->vs && !self->borrowed) {
delete self->vs;
}
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *ViewShape_repr(BPy_ViewShape *self)
{
return PyUnicode_FromFormat("ViewShape - address: %p", self->vs);
}
PyDoc_STRVAR(
/* Wrap. */
ViewShape_add_edge_doc,
".. method:: add_edge(edge)\n"
"\n"
" Adds a ViewEdge to the list of ViewEdge objects.\n"
"\n"
" :param edge: A ViewEdge object.\n"
" :type edge: :class:`ViewEdge`\n");
static PyObject *ViewShape_add_edge(BPy_ViewShape *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"edge", nullptr};
PyObject *py_ve = nullptr;
if (PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist, &ViewEdge_Type, &py_ve)) {
return nullptr;
}
self->vs->AddEdge(((BPy_ViewEdge *)py_ve)->ve);
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
ViewShape_add_vertex_doc,
".. method:: add_vertex(vertex)\n"
"\n"
" Adds a ViewVertex to the list of the ViewVertex objects.\n"
"\n"
" :param vertex: A ViewVertex object.\n"
" :type vertex: :class:`ViewVertex`\n");
static PyObject *ViewShape_add_vertex(BPy_ViewShape *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"vertex", nullptr};
PyObject *py_vv = nullptr;
if (PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist, &ViewVertex_Type, &py_vv)) {
return nullptr;
}
self->vs->AddVertex(((BPy_ViewVertex *)py_vv)->vv);
Py_RETURN_NONE;
}
// virtual ViewShape *duplicate()
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wcast-function-type"
# else
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
# endif
#endif
static PyMethodDef BPy_ViewShape_methods[] = {
{"add_edge",
(PyCFunction)ViewShape_add_edge,
METH_VARARGS | METH_KEYWORDS,
ViewShape_add_edge_doc},
{"add_vertex",
(PyCFunction)ViewShape_add_vertex,
METH_VARARGS | METH_KEYWORDS,
ViewShape_add_vertex_doc},
{nullptr, nullptr, 0, nullptr},
};
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic pop
# else
# pragma GCC diagnostic pop
# endif
#endif
/*----------------------ViewShape get/setters ----------------------------*/
PyDoc_STRVAR(
/* Wrap. */
ViewShape_sshape_doc,
"The SShape on top of which this ViewShape is built.\n"
"\n"
":type: :class:`SShape`\n");
static PyObject *ViewShape_sshape_get(BPy_ViewShape *self, void * /*closure*/)
{
SShape *ss = self->vs->sshape();
if (!ss) {
Py_RETURN_NONE;
}
return BPy_SShape_from_SShape(*ss);
}
static int ViewShape_sshape_set(BPy_ViewShape *self, PyObject *value, void * /*closure*/)
{
if (!BPy_SShape_Check(value)) {
PyErr_SetString(PyExc_TypeError, "value must be an SShape");
return -1;
}
BPy_SShape *py_ss = (BPy_SShape *)value;
self->vs->setSShape(py_ss->ss);
if (self->py_ss) {
Py_DECREF(self->py_ss);
}
if (!py_ss->borrowed) {
self->py_ss = py_ss;
Py_INCREF(self->py_ss);
}
return 0;
}
PyDoc_STRVAR(
/* Wrap. */
ViewShape_vertices_doc,
"The list of ViewVertex objects contained in this ViewShape.\n"
"\n"
":type: list[:class:`ViewVertex`]\n");
static PyObject *ViewShape_vertices_get(BPy_ViewShape *self, void * /*closure*/)
{
vector<ViewVertex *> vertices = self->vs->vertices();
vector<ViewVertex *>::iterator it;
PyObject *py_vertices = PyList_New(vertices.size());
uint i = 0;
for (it = vertices.begin(); it != vertices.end(); it++) {
PyList_SET_ITEM(py_vertices, i++, Any_BPy_ViewVertex_from_ViewVertex(*(*it)));
}
return py_vertices;
}
static int ViewShape_vertices_set(BPy_ViewShape *self, PyObject *value, void * /*closure*/)
{
PyObject *item;
vector<ViewVertex *> v;
if (!PyList_Check(value)) {
PyErr_SetString(PyExc_TypeError, "value must be a list of ViewVertex objects");
return -1;
}
v.reserve(PyList_GET_SIZE(value));
for (uint i = 0; i < PyList_GET_SIZE(value); i++) {
item = PyList_GET_ITEM(value, i);
if (BPy_ViewVertex_Check(item)) {
v.push_back(((BPy_ViewVertex *)item)->vv);
}
else {
PyErr_SetString(PyExc_TypeError, "value must be a list of ViewVertex objects");
return -1;
}
}
self->vs->setVertices(v);
return 0;
}
PyDoc_STRVAR(
/* Wrap. */
ViewShape_edges_doc,
"The list of ViewEdge objects contained in this ViewShape.\n"
"\n"
":type: list[:class:`ViewEdge`]\n");
static PyObject *ViewShape_edges_get(BPy_ViewShape *self, void * /*closure*/)
{
vector<ViewEdge *> edges = self->vs->edges();
vector<ViewEdge *>::iterator it;
PyObject *py_edges = PyList_New(edges.size());
uint i = 0;
for (it = edges.begin(); it != edges.end(); it++) {
PyList_SET_ITEM(py_edges, i++, BPy_ViewEdge_from_ViewEdge(*(*it)));
}
return py_edges;
}
static int ViewShape_edges_set(BPy_ViewShape *self, PyObject *value, void * /*closure*/)
{
PyObject *item;
vector<ViewEdge *> v;
if (!PyList_Check(value)) {
PyErr_SetString(PyExc_TypeError, "value must be a list of ViewEdge objects");
return -1;
}
v.reserve(PyList_GET_SIZE(value));
for (int i = 0; i < PyList_GET_SIZE(value); i++) {
item = PyList_GET_ITEM(value, i);
if (BPy_ViewEdge_Check(item)) {
v.push_back(((BPy_ViewEdge *)item)->ve);
}
else {
PyErr_SetString(PyExc_TypeError, "argument must be list of ViewEdge objects");
return -1;
}
}
self->vs->setEdges(v);
return 0;
}
PyDoc_STRVAR(
/* Wrap. */
ViewShape_name_doc,
"The name of the ViewShape.\n"
"\n"
":type: str\n");
static PyObject *ViewShape_name_get(BPy_ViewShape *self, void * /*closure*/)
{
return blender::PyC_UnicodeFromStdStr(self->vs->getName());
}
PyDoc_STRVAR(
/* Wrap. */
ViewShape_library_path_doc,
"The library path of the ViewShape, or None if the ViewShape is not part of\n"
"a library.\n"
"\n"
":type: str | None\n");
static PyObject *ViewShape_library_path_get(BPy_ViewShape *self, void * /*closure*/)
{
return blender::PyC_UnicodeFromStdStr(self->vs->getLibraryPath());
}
PyDoc_STRVAR(
/* Wrap. */
ViewShape_id_doc,
"The Id of this ViewShape.\n"
"\n"
":type: :class:`Id`\n");
static PyObject *ViewShape_id_get(BPy_ViewShape *self, void * /*closure*/)
{
Id id(self->vs->getId());
return BPy_Id_from_Id(id); // return a copy
}
static PyGetSetDef BPy_ViewShape_getseters[] = {
{"sshape",
(getter)ViewShape_sshape_get,
(setter)ViewShape_sshape_set,
ViewShape_sshape_doc,
nullptr},
{"vertices",
(getter)ViewShape_vertices_get,
(setter)ViewShape_vertices_set,
ViewShape_vertices_doc,
nullptr},
{"edges",
(getter)ViewShape_edges_get,
(setter)ViewShape_edges_set,
ViewShape_edges_doc,
nullptr},
{"name", (getter)ViewShape_name_get, (setter) nullptr, ViewShape_name_doc, nullptr},
{"library_path",
(getter)ViewShape_library_path_get,
(setter) nullptr,
ViewShape_library_path_doc,
nullptr},
{"id", (getter)ViewShape_id_get, (setter) nullptr, ViewShape_id_doc, nullptr},
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
};
/*-----------------------BPy_ViewShape type definition ------------------------------*/
PyTypeObject ViewShape_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "ViewShape",
/*tp_basicsize*/ sizeof(BPy_ViewShape),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ (destructor)ViewShape_dealloc,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ (reprfunc)ViewShape_repr,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ nullptr,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
/*tp_doc*/ ViewShape_doc,
/*tp_traverse*/ nullptr,
/*tp_clear*/ nullptr,
/*tp_richcompare*/ nullptr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ BPy_ViewShape_methods,
/*tp_members*/ nullptr,
/*tp_getset*/ BPy_ViewShape_getseters,
/*tp_base*/ nullptr,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ (initproc)ViewShape_init,
/*tp_alloc*/ nullptr,
/*tp_new*/ PyType_GenericNew,
};
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,37 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#pragma once
extern "C" {
#include <Python.h>
}
#include "../view_map/ViewMap.h"
#include "BPy_SShape.h"
///////////////////////////////////////////////////////////////////////////////////////////
extern PyTypeObject ViewShape_Type;
#define BPy_ViewShape_Check(v) (PyObject_IsInstance((PyObject *)v, (PyObject *)&ViewShape_Type))
/*---------------------------Python BPy_ViewShape structure definition----------*/
struct BPy_ViewShape {
PyObject_HEAD
Freestyle::ViewShape *vs;
bool borrowed; /* true if *vs a borrowed object */
BPy_SShape *py_ss;
};
/*---------------------------Python BPy_ViewShape visible prototypes-----------*/
int ViewShape_Init(PyObject *module);
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,86 @@
/* SPDX-FileCopyrightText: 2004-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
*/
#include "BPy_FalseBP1D.h"
using namespace Freestyle;
///////////////////////////////////////////////////////////////////////////////////////////
//------------------------INSTANCE METHODS ----------------------------------
PyDoc_STRVAR(
/* Wrap. */
FalseBP1D___doc__,
"Class hierarchy: :class:`freestyle.types.BinaryPredicate1D` > :class:`FalseBP1D`\n"
"\n"
".. method:: __call__(inter1, inter2)\n"
"\n"
" Always returns false.\n"
"\n"
" :param inter1: The first Interface1D object.\n"
" :type inter1: :class:`freestyle.types.Interface1D`\n"
" :param inter2: The second Interface1D object.\n"
" :type inter2: :class:`freestyle.types.Interface1D`\n"
" :return: False.\n"
" :rtype: bool\n");
static int FalseBP1D___init__(BPy_FalseBP1D *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {nullptr};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "", (char **)kwlist)) {
return -1;
}
self->py_bp1D.bp1D = new Predicates1D::FalseBP1D();
return 0;
}
/*-----------------------BPy_FalseBP1D type definition ------------------------------*/
PyTypeObject FalseBP1D_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "FalseBP1D",
/*tp_basicsize*/ sizeof(BPy_FalseBP1D),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ nullptr,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ nullptr,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ nullptr,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
/*tp_doc*/ FalseBP1D___doc__,
/*tp_traverse*/ nullptr,
/*tp_clear*/ nullptr,
/*tp_richcompare*/ nullptr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ nullptr,
/*tp_members*/ nullptr,
/*tp_getset*/ nullptr,
/*tp_base*/ &BinaryPredicate1D_Type,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ (initproc)FalseBP1D___init__,
/*tp_alloc*/ nullptr,
/*tp_new*/ nullptr,
};
///////////////////////////////////////////////////////////////////////////////////////////

Some files were not shown because too many files have changed in this diff Show More