Files
test/source/blender/freestyle/intern/stroke/Stroke.h
2025-07-17 12:30:39 +10:00

873 lines
22 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Classes to define a stroke
*/
#include <map>
#include <vector>
#include "Curve.h"
#include "../view_map/Interface1D.h"
#include "../view_map/Silhouette.h"
#include "../system/FreestyleConfig.h"
#include "../system/StringUtils.h"
#include "MEM_guardedalloc.h"
extern "C" {
struct MTex;
struct bNodeTree;
}
#ifndef MAX_MTEX
# define MAX_MTEX 18
#endif
namespace Freestyle {
//
// StrokeAttribute
//
////////////////////////////////////////////////////////
/** Class to define an attribute associated to a Stroke Vertex.
* This attribute stores the color, alpha and thickness values for a Stroke Vertex.
*/
class StrokeAttribute {
public:
/** default constructor */
StrokeAttribute();
/** Copy constructor */
StrokeAttribute(const StrokeAttribute &iBrother);
/** Builds a stroke vertex attribute from a set of parameters.
* \param iRColor:
* The Red Component value.
* \param iGColor:
* The Green Component value.
* \param iBColor:
* The Blue Component value.
* \param iAlpha:
* The transparency value
* \param iRThickness:
* The thickness of the stroke on the right
* \param iLThickness:
* The Thickness of the stroke on the left
*/
StrokeAttribute(float iRColor,
float iGColor,
float iBColor,
float iAlpha,
float iRThickness,
float iLThickness);
/** Interpolation constructor.
* Builds a StrokeAttribute from two StrokeAttributes and an interpolation parameter.
* \param a1:
* The first Attribute.
* \param a2:
* The second parameter.
* \param t:
* The interpolation parameter.
*/
StrokeAttribute(const StrokeAttribute &a1, const StrokeAttribute &a2, float t);
/** destructor */
virtual ~StrokeAttribute();
/* operators */
/** operator = */
StrokeAttribute &operator=(const StrokeAttribute &iBrother);
/* accessors */
/** Returns the attribute's color.
* \return The array of 3 floats containing the R,G,B values of the attribute's color.
*/
inline const float *getColor() const
{
return _color;
}
/** Returns the R color component. */
inline float getColorR() const
{
return _color[0];
}
/** Returns the G color component. */
inline float getColorG() const
{
return _color[1];
}
/** Returns the B color component. */
inline float getColorB() const
{
return _color[2];
}
/** Returns the RGB color components. */
inline Vec3f getColorRGB() const
{
return Vec3f(_color[0], _color[1], _color[2]);
}
/** Returns the alpha color component. */
inline float getAlpha() const
{
return _alpha;
}
/** Returns the attribute's thickness.
* \return an array of 2 floats. the first value is the thickness on the right of the vertex
* when following the stroke, the second one is the thickness on the left.
*/
inline const float *getThickness() const
{
return _thickness;
}
/** Returns the thickness on the right of the vertex when following the stroke. */
inline float getThicknessR() const
{
return _thickness[0];
}
/** Returns the thickness on the left of the vertex when following the stroke. */
inline float getThicknessL() const
{
return _thickness[1];
}
/** Returns the thickness on the right and on the left of the vertex when following the stroke.
*/
inline Vec2f getThicknessRL() const
{
return Vec2f(_thickness[0], _thickness[1]);
}
/** Returns true if the strokevertex is visible, false otherwise */
inline bool isVisible() const
{
return _visible;
}
/** Returns an attribute of type real
* \param iName:
* The name of the attribute
*/
float getAttributeReal(const char *iName) const;
/** Returns an attribute of type Vec2f
* \param iName:
* The name of the attribute
*/
Vec2f getAttributeVec2f(const char *iName) const;
/** Returns an attribute of type Vec3f
* \param iName:
* The name of the attribute
*/
Vec3f getAttributeVec3f(const char *iName) const;
/** Checks whether the attribute iName is available */
bool isAttributeAvailableReal(const char *iName) const;
/** Checks whether the attribute iName is available */
bool isAttributeAvailableVec2f(const char *iName) const;
/** Checks whether the attribute iName is available */
bool isAttributeAvailableVec3f(const char *iName) const;
/* modifiers */
/** sets the attribute's color.
* \param r:
* The new R value.
* \param g:
* The new G value.
* \param b:
* The new B value.
*/
inline void setColor(float r, float g, float b)
{
_color[0] = r;
_color[1] = g;
_color[2] = b;
}
/** sets the attribute's color.
* \param iRGB:
* The new RGB values.
*/
inline void setColor(const Vec3f &iRGB)
{
_color[0] = iRGB[0];
_color[1] = iRGB[1];
_color[2] = iRGB[2];
}
/** sets the attribute's alpha value.
* \param alpha:
* The new alpha value.
*/
inline void setAlpha(float alpha)
{
_alpha = alpha;
}
/** sets the attribute's thickness.
* \param tr:
* The thickness on the right of the vertex when following the stroke.
* \param tl:
* The thickness on the left of the vertex when following the stroke.
*/
inline void setThickness(float tr, float tl)
{
_thickness[0] = tr;
_thickness[1] = tl;
}
/** sets the attribute's thickness.
* \param tRL:
* The thickness on the right and on the left of the vertex when following the stroke.
*/
inline void setThickness(const Vec2f &tRL)
{
_thickness[0] = tRL[0];
_thickness[1] = tRL[1];
}
/** sets the visible flag. True means visible. */
inline void setVisible(bool iVisible)
{
_visible = iVisible;
}
/** Adds a user defined attribute of type real
* If there is no attribute of name iName, it is added.
* Otherwise, the new value replaces the old one.
* \param iName:
* The name of the attribute
* \param att:
* The attribute's value
*/
void setAttributeReal(const char *iName, float att);
/** Adds a user defined attribute of type Vec2f
* If there is no attribute of name iName, it is added.
* Otherwise, the new value replaces the old one.
* \param iName:
* The name of the attribute
* \param att:
* The attribute's value
*/
void setAttributeVec2f(const char *iName, const Vec2f &att);
/** Adds a user defined attribute of type Vec3f
* If there is no attribute of name iName, it is added.
* Otherwise, the new value replaces the old one.
* \param iName:
* The name of the attribute
* \param att:
* The attribute's value
*/
void setAttributeVec3f(const char *iName, const Vec3f &att);
private:
typedef std::map<const char *, float, StringUtils::ltstr> realMap;
typedef std::map<const char *, Vec2f, StringUtils::ltstr> Vec2fMap;
typedef std::map<const char *, Vec3f, StringUtils::ltstr> Vec3fMap;
//! the color
float _color[3];
//! alpha
float _alpha;
//! the thickness on the right and on the left of the backbone vertex (the stroke is oriented)
float _thickness[2];
bool _visible;
realMap *_userAttributesReal;
Vec2fMap *_userAttributesVec2f;
Vec3fMap *_userAttributesVec3f;
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:StrokeAttribute")
};
//
// StrokeVertex
//
////////////////////////////////////////////////////////
/** Class to define a stroke vertex. */
class StrokeVertex : public CurvePoint {
public: // Implementation of Interface0D
/** Returns the string "StrokeVertex" */
virtual string getExactTypeName() const
{
return "StrokeVertex";
}
private:
StrokeAttribute _Attribute; //! The attribute associated to the vertex
float _CurvilignAbscissa; //! the curvilign abscissa
float _StrokeLength; // stroke length
public:
/** default constructor */
StrokeVertex();
/** Copy constructor */
StrokeVertex(const StrokeVertex &iBrother);
/** Builds a stroke vertex from a SVertex */
StrokeVertex(SVertex *iSVertex);
/** Builds a stroke vertex from a CurvePoint */
StrokeVertex(CurvePoint *iPoint);
/** Builds Stroke Vertex from 2 stroke vertices and an interpolation parameter. */
StrokeVertex(StrokeVertex *iA, StrokeVertex *iB, float t3);
/** Builds a stroke from a view vertex and an attribute */
StrokeVertex(SVertex *iSVertex, const StrokeAttribute &iAttribute);
/* operators */
/** operator = */
StrokeVertex &operator=(const StrokeVertex &iBrother);
/* accessors */
/** Returns the 2D point x coordinate */
inline real x() const
{
return _Point2d[0];
}
/** Returns the 2D point y coordinate */
inline real y() const
{
return _Point2d[1];
}
/** Returns the 2D point coordinates as a Vec2r */
inline Vec2r getPoint() const
{
return getPoint2D();
}
/** Returns the i-th 2D point coordinate (i=0 or 1). */
inline real operator[](const int i) const
{
return _Point2d[i];
}
/** Returns the StrokeAttribute for this StrokeVertex */
inline const StrokeAttribute &attribute() const
{
return _Attribute;
}
/** Returns a non-const reference to the StrokeAttribute of this StrokeVertex */
inline StrokeAttribute &attribute()
{
return _Attribute;
}
/** Returns the curvilinear abscissa */
inline float curvilinearAbscissa() const
{
return _CurvilignAbscissa;
}
/** Returns the length of the Stroke to which this StrokeVertex belongs */
inline float strokeLength() const
{
return _StrokeLength;
}
/** Returns the curvilinear abscissa of this StrokeVertex in the Stroke */
inline float u() const
{
return _CurvilignAbscissa / _StrokeLength;
}
/* modifiers */
/** sets the 2D x value */
inline void setX(real x)
{
_Point2d[0] = x;
}
/** sets the 2D y value */
inline void setY(real y)
{
_Point2d[1] = y;
}
/** sets the 2D x and y values */
inline void setPoint(real x, real y)
{
_Point2d[0] = x;
_Point2d[1] = y;
}
/** sets the 2D x and y values */
inline void setPoint(const Vec2r &p)
{
_Point2d[0] = p[0];
_Point2d[1] = p[1];
}
/** Returns a reference to the i-th 2D point coordinate (i=0 or 1). */
inline real &operator[](const int i)
{
return _Point2d[i];
}
/** sets the attribute. */
inline void setAttribute(const StrokeAttribute &iAttribute)
{
_Attribute = iAttribute;
}
/** sets the curvilinear abscissa of this StrokeVertex in the Stroke */
inline void setCurvilinearAbscissa(float iAbscissa)
{
_CurvilignAbscissa = iAbscissa;
}
/** sets the Stroke's length (it's only a value stored by the Stroke Vertex, it won't change the
* real Stroke's length.)
*/
inline void setStrokeLength(float iLength)
{
_StrokeLength = iLength;
}
/* interface definition */
/* inherited */
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:StrokeVertex")
};
//
// Stroke
//
////////////////////////////////////////////////////////
class StrokeRenderer;
class StrokeRep;
namespace StrokeInternal {
class vertex_const_traits;
class vertex_nonconst_traits;
template<class Traits> class vertex_iterator_base;
class StrokeVertexIterator;
} // end of namespace StrokeInternal
/** Class to define a stroke.
* A stroke is made of a set of 2D vertices (StrokeVertex), regularly spaced out.
* This set of vertices defines the stroke's backbone geometry.
* Each of these stroke vertices defines the stroke's shape and appearance at this vertex
* position.
*/
class Stroke : public Interface1D {
public: // Implementation of Interface1D
/** Returns the string "Stroke" */
virtual string getExactTypeName() const
{
return "Stroke";
}
// Data access methods
/** Returns the Id of the Stroke */
virtual Id getId() const
{
return _id;
}
/** The different blending modes available to simulate the interaction media-medium. */
typedef enum {
DRY_MEDIUM, /**< To simulate a dry medium such as Pencil or Charcoal. */
HUMID_MEDIUM, /**< To simulate ink painting (color subtraction blending). */
OPAQUE_MEDIUM, /**< To simulate an opaque medium (oil, spray...). */
} MediumType;
public:
typedef std::deque<StrokeVertex *> vertex_container; // the vertices container
typedef std::vector<ViewEdge *> viewedge_container; // the viewedges container
typedef StrokeInternal::vertex_iterator_base<StrokeInternal::vertex_nonconst_traits>
vertex_iterator;
typedef StrokeInternal::vertex_iterator_base<StrokeInternal::vertex_const_traits>
const_vertex_iterator;
public:
// typedef StrokeVertex vertex_type;
private:
vertex_container _Vertices; //! The stroke's backbone vertices
Id _id;
float _Length; // The stroke length
viewedge_container _ViewEdges;
float _sampling;
float _textureStep;
// StrokeRenderer *_renderer; // mark implementation OpenGL renderer
MediumType _mediumType;
uint _textureId;
MTex *_mtex[MAX_MTEX];
bNodeTree *_nodeTree;
bool _tips;
StrokeRep *_rep;
Vec2r _extremityOrientations[2]; // the orientations of the first and last extermity
public:
/** default constructor */
Stroke();
/** copy constructor */
Stroke(const Stroke &iBrother);
/** Builds a stroke from a set of StrokeVertex.
* This constructor is templated by an iterator type.
* This iterator type must allow the vertices parsing using the ++ operator.
* \param iBegin:
* The iterator pointing to the first vertex.
* \param iEnd:
* The iterator pointing to the end of the vertex list.
*/
template<class InputVertexIterator> Stroke(InputVertexIterator iBegin, InputVertexIterator iEnd);
/** Destructor */
virtual ~Stroke();
/* operators */
/** operator = */
Stroke &operator=(const Stroke &iBrother);
/** Compute the sampling needed to get iNVertices vertices.
* If the specified number of vertices is less than the actual number of vertices, the actual
* sampling value is returned. (To remove Vertices, use the RemoveVertex() method of this class).
* \param iNVertices:
* The number of StrokeVertices we eventually want in our Stroke.
* \return the sampling that must be used in the Resample(float) method.
* \see Resample(int)
* \see Resample(float)
*/
float ComputeSampling(int iNVertices);
/** Resampling method.
* Resamples the curve so that it eventually has iNPoints. That means it is going to add
* iNPoints-vertices_size, if vertices_size is the number of points we already have. If
* vertices_size >= iNPoints, no resampling is done.
* \param iNPoints: The number of vertices we
* eventually want in our stroke.
*/
int Resample(int iNPoints);
/** Resampling method.
* Resamples the curve with a given sampling.
* If this sampling is < to the actual sampling value, no resampling is done.
* \param iSampling:
* The new sampling value.
*/
int Resample(float iSampling);
/** Removes all vertices from the Stroke.
*/
void RemoveAllVertices();
/** Removes the stroke vertex iVertex
* from the stroke.
* The length and curvilinear abscissa are updated
* consequently.
*/
void RemoveVertex(StrokeVertex *iVertex);
/** Inserts the stroke vertex iVertex in the stroke before next.
* The length, curvilinear abscissa are updated consequently.
* \param iVertex:
* The StrokeVertex to insert in the Stroke.
* \param next:
* A StrokeVertexIterator pointing to the StrokeVertex before which iVertex must be inserted.
*/
void InsertVertex(StrokeVertex *iVertex, StrokeInternal::StrokeVertexIterator next);
/** Updates the 2D length of the Stroke */
void UpdateLength();
/* Render method */
void ScaleThickness(float iFactor);
void Render(const StrokeRenderer *iRenderer);
void RenderBasic(const StrokeRenderer *iRenderer);
/* Iterator definition */
/* accessors */
/** Returns the 2D length of the Stroke */
inline real getLength2D() const
{
return _Length;
}
/** Returns a reference to the time stamp value of the stroke. */
/** Returns the MediumType used for this Stroke. */
inline MediumType getMediumType() const
{
return _mediumType;
}
/** Returns the id of the texture used to simulate th marks system for this Stroke */
inline uint getTextureId()
{
return _textureId;
}
/** Returns the spacing of texture coordinates along the stroke length */
inline float getTextureStep()
{
return _textureStep;
}
/** Returns the texture used at given index to simulate the marks system for this Stroke */
inline MTex *getMTex(int idx)
{
return _mtex[idx];
}
/** Return the shader node tree to define textures. */
inline bNodeTree *getNodeTree()
{
return _nodeTree;
}
/** Returns true if this Stroke has textures assigned, false otherwise. */
inline bool hasTex() const
{
return (_mtex[0] != nullptr) || _nodeTree;
}
/** Returns true if this Stroke uses a texture with tips, false otherwise. */
inline bool hasTips() const
{
return _tips;
}
/* these advanced iterators are used only in C++ */
inline int vertices_size() const
{
return _Vertices.size();
}
inline viewedge_container::const_iterator viewedges_begin() const
{
return _ViewEdges.begin();
}
inline viewedge_container::iterator viewedges_begin()
{
return _ViewEdges.begin();
}
inline viewedge_container::const_iterator viewedges_end() const
{
return _ViewEdges.end();
}
inline viewedge_container::iterator viewedges_end()
{
return _ViewEdges.end();
}
inline int viewedges_size() const
{
return _ViewEdges.size();
}
inline Vec2r getBeginningOrientation() const
{
return _extremityOrientations[0];
}
inline real getBeginningOrientationX() const
{
return _extremityOrientations[0].x();
}
inline real getBeginningOrientationY() const
{
return _extremityOrientations[0].y();
}
inline Vec2r getEndingOrientation() const
{
return _extremityOrientations[1];
}
inline real getEndingOrientationX() const
{
return _extremityOrientations[1].x();
}
inline real getEndingOrientationY() const
{
return _extremityOrientations[1].y();
}
/* modifiers */
/** sets the Id of the Stroke. */
inline void setId(const Id &id)
{
_id = id;
}
/** sets the 2D length of the Stroke. */
void setLength(float iLength);
/** sets the medium type that must be used for this Stroke. */
inline void setMediumType(MediumType iType)
{
_mediumType = iType;
}
/** sets the texture id to be used to simulate the marks system for this Stroke. */
inline void setTextureId(uint id)
{
_textureId = id;
}
/** sets the spacing of texture coordinates along the stroke length. */
inline void setTextureStep(float step)
{
_textureStep = step;
}
/** assigns a blender texture to the first available slot. */
inline int setMTex(MTex *mtex)
{
for (int a = 0; a < MAX_MTEX; a++) {
if (!_mtex[a]) {
_mtex[a] = mtex;
return 0;
}
}
return -1; /* no free slots */
}
/** assigns a node tree (of new shading nodes) to define textures. */
inline void setNodeTree(bNodeTree *iNodeTree)
{
_nodeTree = iNodeTree;
}
/** sets the flag telling whether this stroke is using a texture with tips or not. */
inline void setTips(bool iTips)
{
_tips = iTips;
}
inline void push_back(StrokeVertex *iVertex)
{
_Vertices.push_back(iVertex);
}
inline void push_front(StrokeVertex *iVertex)
{
_Vertices.push_front(iVertex);
}
inline void AddViewEdge(ViewEdge *iViewEdge)
{
_ViewEdges.push_back(iViewEdge);
}
inline void setBeginningOrientation(const Vec2r &iOrientation)
{
_extremityOrientations[0] = iOrientation;
}
inline void setBeginningOrientation(real x, real y)
{
_extremityOrientations[0] = Vec2r(x, y);
}
inline void setEndingOrientation(const Vec2r &iOrientation)
{
_extremityOrientations[1] = iOrientation;
}
inline void setEndingOrientation(real x, real y)
{
_extremityOrientations[1] = Vec2r(x, y);
}
/* Information access interface */
// embedding vertex iterator
const_vertex_iterator vertices_begin() const;
vertex_iterator vertices_begin(float sampling = 0.0f);
const_vertex_iterator vertices_end() const;
vertex_iterator vertices_end();
/** Returns a StrokeVertexIterator pointing on the first StrokeVertex of the Stroke. One can
* specify a sampling value to re-sample the Stroke on the fly if needed.
*
* \param t: The resampling value with which we want our Stroke to be resampled.
* If 0 is specified, no resampling is done.
*/
StrokeInternal::StrokeVertexIterator strokeVerticesBegin(float t = 0.0f);
/** Returns a StrokeVertexIterator pointing after the last StrokeVertex of the Stroke. */
StrokeInternal::StrokeVertexIterator strokeVerticesEnd();
/** Returns the number of StrokeVertex constituting the Stroke. */
inline uint strokeVerticesSize() const
{
return _Vertices.size();
}
/** Returns the i-th StrokeVertex constituting the Stroke. */
inline StrokeVertex &strokeVerticeAt(uint i)
{
return *(_Vertices.at(i));
}
// Iterator access (Interface1D)
/** Returns an Interface0DIterator pointing on the first StrokeVertex of the Stroke. */
virtual Interface0DIterator verticesBegin();
/** Returns an Interface0DIterator pointing after the last StrokeVertex of the Stroke. */
virtual Interface0DIterator verticesEnd();
virtual Interface0DIterator pointsBegin(float t = 0.0f);
virtual Interface0DIterator pointsEnd(float t = 0.0f);
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:Stroke")
};
//
// Implementation
//
////////////////////////////////////////////////////////
template<class InputVertexIterator>
Stroke::Stroke(InputVertexIterator iBegin, InputVertexIterator iEnd)
{
for (InputVertexIterator v = iBegin, vend = iEnd; v != vend; v++) {
_Vertices.push_back(*v);
}
_Length = 0;
_id = 0;
}
} /* namespace Freestyle */