/////////////////////////////////////////////////////////////
// Cinema 4D SDK                                           //
/////////////////////////////////////////////////////////////
// (c) MAXON Computer GmbH, all rights reserved            //
/////////////////////////////////////////////////////////////

#ifndef C4D_SNAPDATA_H__
#define C4D_SNAPDATA_H__

#include "description/ocamera.h"
#include "c4d_basedata.h"
#include "description/dmodeling.h"
#include "maxon/basearray.h"

class String;
class BaseList2D;
class BaseDocument;
class BaseDraw;
class ViewportSelect;
class AtomArray;
class BaseThread;
class BaseDrawHelp;
class BaseBitmap;
class BaseContainer;
class EditorWindow;
class BaseObject;

/// @addtogroup Snapping
/// @{
/// Snap system has been completely redesigned and rewritten in @em R14.\n
/// The new snapping API is made of powerful classes and structs:
/// - SnapData: Data class to develop snap mode plugins.
/// - SnapCore: The snap interface with utilities functions.
///
/// Data structures:
/// - SnapStruct
/// - SnapResult
/// - SnapPoint
///
/// Here is how we could use the old snapping API, i.e. @c DetectSnapping class:
/// @code
/// AutoAlloc<DetectSnapping> snap;
/// if (!snap)
/// 	return false;
///
/// snap->Init(doc, bd, nullptr, Vector(), 0, false, true, true);
/// Vector delta;
/// if(snap->IsEnabled(doc))
/// {
/// 	if (snap->SnapTo(pos, &delta))
/// 	{
/// 		pos += delta;
/// 	}
/// }
/// @endcode
/// @c DetectSnapping has been removed and now the interface class for snapping is SnapCore.\n
/// Here is the above code converted to use the new snapping API in @em R14:
/// @code
/// SnapResult res;
/// AutoAlloc<SnapCore> snap;
/// if (!snap)
/// 	return false;
///
/// snap->Init(doc, bd);
/// if (IsSnapEnabled(doc))
/// {
/// 	if (snap->Snap(pos, res))
/// 	{
/// 		pos += res.delta;
/// 	}
/// }
/// @endcode

/// SnapCore library ID.
#define ID_SNAPCORE_LIB	440000112

/// @addtogroup PLUGINFLAG_SNAP
/// @ingroup group_enumeration
/// @{
#define PLUGINFLAG_SNAP_INFERRED_POINT	(1 << 27)			/// Inferred points can be generated by the results of this snap mode (used in registration).
#define PLUGINFLAG_SNAP_INFERRED_AXIS		(1 << 26)			/// Inferred axis can be generated by the results of this snap mode (used in registration).
#define PLUGINFLAG_SNAP_MIDPOINT				(1 << 25)			/// This snap mode is used by the midpoint snap, use the parent id to set which snap it belongs to.
/// @}

/// @addtogroup SNAPPRIORITY
/// @ingroup group_enumeration
/// @{
/// Snap priority. A higher priority snap will take precedence over a lower priority one when both are within snap range.
enum class SNAPPRIORITY
{
	NONE		= 0,				///< The lowest priority
	PLANE		= 1000,			///< Planar snapping e.g. to the workplane or a polygon surface.
	GUIDE		= 2000,			///< Guide snapping lower then edge to allow correct drawing for dynamic guides.
	EDGE		= 3000,			///< Edge snapping is a slightly higher priority and will override planar snapping.
	POINT		= 4000			///< Point snapping is the highest (default) snapping priority.
} MAXON_ENUM_ORDERED_LIST(SNAPPRIORITY);
/// @}

/// @addtogroup SNAPFLAGS
/// @ingroup group_enumeration
/// @{
/// Snap flags. Sent during the snap process itself to limit snapping.
enum class SNAPFLAGS
{
	NONE						= 0,						///< No flags.
	IGNORE_SELECTED	= (1 << 0),			///< Do not snap to the selected objects or components.
	IGNORE_AXIS			= (1 << 1),			///< Do not snap to the active axis.
	POINTSONLY			= (1 << 2),			///< Ignore modes with lower priority than points.
	SURFACEONLY			= (1 << 3)			///< Ignore modes with higher priority than planes.
} MAXON_ENUM_FLAGS(SNAPFLAGS);
/// @}

/// @addtogroup SNAPFLAGS
/// @ingroup group_enumeration
/// @{
/// Inferred guide types.
enum class INFERREDGUIDETYPE
{
	POINT	= 0,			///< An inferred point or "axis", create guides along all perpendicular lines to the point in @em x, @em y and @em z.
	LINE	= 1,			///< Inferred guide line, act just like a normal guide line, e.g. along an edge.
	PLANE	= 2				///< Inferred plane, act just like a normal guide plane.
} MAXON_ENUM_LIST(INFERREDGUIDETYPE);
/// @}

//----------------------------------------------------------------------------------------
/// Structure for holding and returning information about the result of a snap and the point that has been snapped to.
//----------------------------------------------------------------------------------------
struct SnapPoint
{
public:
	//----------------------------------------------------------------------------------------
	/// Default constructor.
	//----------------------------------------------------------------------------------------
	SnapPoint() : target(nullptr), component(NOTOK) { }

	//----------------------------------------------------------------------------------------
	/// Destructor.
	//----------------------------------------------------------------------------------------
	~SnapPoint() { }

	Matrix			mat;				///< The result position of the snap.
	BaseList2D*	target;			///< The target object being snapped to.
	Int32				component;	///< The component ID being snapped to (e.g. polygon, edge, point, line point).
};

//----------------------------------------------------------------------------------------
/// The final result that the user gets when calling SnapCore::Snap or SnapCore::Intersect.
//----------------------------------------------------------------------------------------
struct SnapResult : public SnapPoint
{
public:
	//----------------------------------------------------------------------------------------
	/// Default constructor.
	//----------------------------------------------------------------------------------------
	SnapResult() : SnapPoint(), snapmode(NOTOK), initial_snap(Vector(0.0)) { }

	//----------------------------------------------------------------------------------------
	/// Destructor.
	//----------------------------------------------------------------------------------------
	~SnapResult() { }

	Int32		snapmode;				///< The snap mode used.
	Vector	initial_snap;		///< The snap position before 2D processing.
	Vector	delta;					///< The delta between new and old point positions.
};

//----------------------------------------------------------------------------------------
/// Structure containing optional exclusion components.\n
/// Allows to avoid snap on contained components independently to the selections and to the Document mode.
//----------------------------------------------------------------------------------------
struct SnapExclude
{
	MAXON_DISALLOW_COPY_AND_ASSIGN(SnapExclude);
public:

	//----------------------------------------------------------------------------------------
	/// Constructor.
	//----------------------------------------------------------------------------------------
	SnapExclude() { }

	//----------------------------------------------------------------------------------------
	/// Copy constructor.
	/// @param[in] src								The source SnapExclude.
	//----------------------------------------------------------------------------------------
	SnapExclude(SnapExclude&& src)
	{
		op = src.op;
		points = std::move(src.points);
		edges = std::move(src.edges);
		polys = std::move(src.polys);
	}

	//----------------------------------------------------------------------------------------
	/// Assignment operator.
	/// @param[in] src								The source SnapExclude.
	//----------------------------------------------------------------------------------------
	MAXON_OPERATOR_MOVE_ASSIGNMENT(SnapExclude)

	//----------------------------------------------------------------------------------------
	/// Copies the exclude information in @formatParam{src}.
	/// @param[in] src								The source SnapExclude.
	/// @return												@formatConstant{true}
	//----------------------------------------------------------------------------------------
	Bool CopyFrom(const SnapExclude& src)
	{
		op = src.op;
		if (points.CopyFrom(src.points) == maxon::FAILED)
			return false;
		if (edges.CopyFrom(src.edges) == maxon::FAILED)
			return false;
		if (polys.CopyFrom(src.polys) == maxon::FAILED)
			return false;
		return true;
	}

	BaseObject* op;											///< The object.
	maxon::BaseArray<Int32> points;			///< The points list to exclude.
	maxon::BaseArray<Int32> edges;			///< The edges list to exclude.
	maxon::BaseArray<Int32> polys;			///< The polygons list to exclude.
};

//----------------------------------------------------------------------------------------
/// Structure containing relevant context information for the snap system.
//----------------------------------------------------------------------------------------
struct SnapStruct
{
public:
	//----------------------------------------------------------------------------------------
	/// Default constructor.
	//----------------------------------------------------------------------------------------
	SnapStruct() : doc(nullptr), bd(nullptr), snap_radius(10.0), excluded_objects(nullptr), object_list(nullptr), bt(nullptr), flags(SNAPFLAGS::NONE), projection(Pperspective), snapExcludeList(nullptr) { }

	//----------------------------------------------------------------------------------------
	/// Destructor.
	//----------------------------------------------------------------------------------------
	~SnapStruct() { }

	BaseDocument* doc;																	///< The current document.
	BaseDraw*			bd;																		///< The active BaseDraw that snapping will be taking place in.
	Float					snap_radius;													///< The radius to snap within.
	AtomArray*		excluded_objects;											///< A list of objects to exclude.
	AtomArray*		object_list;													///< A list of objects in view.
	BaseThread*		bt;																		///< The current BaseThread.
	SNAPFLAGS			flags;																///< Flags sent by user calling snap function.
	Int32					projection;														///< Cached @formatParam{bd}->@link BaseDraw::GetProjection GetProjection()@endlink but faster.
	maxon::BaseArray<SnapExclude>* snapExcludeList;			///< A pointer to a list of element to exclude per object.
};

//----------------------------------------------------------------------------------------
/// A data class for creating snap mode plugins. For example Polygon Snap, Edge Snap, Spline Snap, Vertex Snap and so on.
///
/// Use RegisterSnapPlugin() to register a snap mode plugin.
//----------------------------------------------------------------------------------------
class SnapData : public BaseData
{
public:
	//----------------------------------------------------------------------------------------
	/// Called to initialize the snap plugin.
	/// @return												@trueIfOtherwiseFalse{successful}
	//----------------------------------------------------------------------------------------
	virtual Bool Init(void) { return true; }

	//----------------------------------------------------------------------------------------
	/// Called to free the snap plugin instance.
	//----------------------------------------------------------------------------------------
	virtual void Free(void) { }

	//----------------------------------------------------------------------------------------
	/// Called to prepare snapping to begin.
	/// @param[in] ss									The passed snap context structure.
	/// @return												@trueIfOtherwiseFalse{successful}
	//----------------------------------------------------------------------------------------
	virtual Bool InitSnap(const SnapStruct& ss) { return true; }

	//----------------------------------------------------------------------------------------
	/// Called for 3D Snap detection.
	/// @param[in] p									The position being snapped to in world coordinates.
	/// @param[in] ss									The passed Snap context structure.
	/// @param[out] result						Assign data about the resulting snap point if one is generated.
	/// @return												@trueIfOtherwiseFalse{a snap occurred}
	//----------------------------------------------------------------------------------------
	virtual Bool Snap(const Vector& p, const SnapStruct& ss, SnapPoint& result) { return false; }

	//----------------------------------------------------------------------------------------
	/// Called for guide intersection.
	/// @param[in] p									The position being snapped to in world coordinates.
	/// @param[in] n									The guide normal in world coordinates.
	/// @param[in] plane							@formatConstant{true} if the guide is a plane, @formatConstant{false} if it is a ray/line.
	/// @param[in] ss									The passed Snap context structure.
	/// @param[out] result						Assign data about the resulting intersection points if they are generated.
	/// @return												@trueIfOtherwiseFalse{an intersection occurred}
	//----------------------------------------------------------------------------------------
	virtual Bool Intersect(const Vector& p, const Vector& n, Bool plane, const SnapStruct& ss, SnapPoint& result){ return false; }

	//----------------------------------------------------------------------------------------
	/// Called to free any stored data after snapping has occurred.
	/// @param[in] ss									The passed Snap context structure.
	//----------------------------------------------------------------------------------------
	virtual void FreeSnap(const SnapStruct& ss) { }

	//----------------------------------------------------------------------------------------
	/// Called for custom draw during snap.
	/// @param[in] ss									The passed Snap context structure.
	/// @param[in] doc								The current document passed by the tool. @callerOwnsPointed{document}
	/// @param[in] bd									The current BaseDraw passed by the tool. @callerOwnsPointed{BaseDraw}
	/// @param[in] bh									The current BaseDrawHelp passed by the tool. @callerOwnsPointed{BaseDrawHelp}
	/// @param[in] bt									The current BaseThread passed by the tool. @callerOwnsPointed{BaseThread}
	/// @return												@trueIfOtherwiseFalse{successful}
	//----------------------------------------------------------------------------------------
	virtual Bool Draw(const SnapStruct& ss, BaseDocument* doc, BaseDraw* bd, BaseDrawHelp* bh, BaseThread* bt) { return true; }

	//----------------------------------------------------------------------------------------
	/// Called to intercept the @link ToolData::GetCursorInfo GetCursorInfo@endlink purely for the snap.
	/// @param[in] ss									The passed Snap context structure.
	/// @param[in] doc								The current document passed by the tool. @callerOwnsPointed{document}
	/// @param[in] bd									The current BaseDraw passed by the tool. @callerOwnsPointed{BaseDraw}
	/// @param[in] x									The mouse x position passed by the tool.
	/// @param[in] y									The mouse y position passed by the tool.
	/// @param[out] result						The message container passed by the tool.
	/// @return												@trueIfOtherwiseFalse{to override the active tool}
	//----------------------------------------------------------------------------------------
	virtual Bool GetCursorInfo(const SnapStruct& ss, BaseDocument* doc, BaseDraw* bd, Float x, Float y, BaseContainer& result) { return false; }

	//----------------------------------------------------------------------------------------
	/// Called to intercept the @link ToolData::MouseInput MouseInput@endlink purely for the snap.
	/// @param[in] ss									The passed Snap context structure.
	/// @param[in] doc								The current document passed by the tool. @callerOwnsPointed{document}
	/// @param[in] bd									The current BaseDraw passed by the tool. @callerOwnsPointed{BaseDraw}
	/// @param[in] win								The current EditorWindow passed by the tool. @callerOwnsPointed{EditorWindow}
	/// @param[in] msg								The message container passed by the tool.
	/// @return												@trueIfOtherwiseFalse{to override the active tool}
	//----------------------------------------------------------------------------------------
	virtual Bool MouseInput(const SnapStruct& ss, BaseDocument* doc, BaseDraw* bd, EditorWindow* win, const BaseContainer& msg){ return false; }

	//----------------------------------------------------------------------------------------
	/// Called to intercept the @link ToolData::KeyboardInput KeyboardInput@endlink purely for the snap.
	/// @param[in] ss									The passed Snap context structure.
	/// @param[in] doc								The current document passed by the tool. @callerOwnsPointed{document}
	/// @param[in] bd									The current BaseDraw passed by the tool. @callerOwnsPointed{BaseDraw}
	/// @param[in] win								The current EditorWindow passed by the tool. @callerOwnsPointed{EditorWindow}
	/// @param[in] msg								The message container passed by the tool.
	/// @return												@trueIfOtherwiseFalse{to override the active tool}
	//----------------------------------------------------------------------------------------
	virtual Bool KeyboardInput(const SnapStruct& ss, BaseDocument* doc, BaseDraw* bd, EditorWindow* win, const BaseContainer& msg) { return false; }
};

//----------------------------------------------------------------------------------------
/// The Snap Interface, must be allocated either with AutoAlloc, or Alloc() and freed after use with Free().
//----------------------------------------------------------------------------------------
class SnapCore
{
private:
	SnapCore() { }

public:

	/// @name Alloc/Free
	/// @{

	//----------------------------------------------------------------------------------------
	/// @allocatesA{snap core}
	/// @return												@allocReturn{snap core}
	//----------------------------------------------------------------------------------------
	static SnapCore* Alloc(void);

	//----------------------------------------------------------------------------------------
	/// @destructsAlloc{snap cores}
	/// @param[in] p									@theToDestruct{snap core}
	//----------------------------------------------------------------------------------------
	static void Free(SnapCore*& p);

	/// @}

	/// @name Snap/Intersect
	/// @{

	//----------------------------------------------------------------------------------------
	/// Snap initialization routine.
	/// @param[in] doc								The current document. @callerOwnsPointed{document}
	/// @param[in] bd									The BaseDraw to initialize. @callerOwnsPointed{BaseDraw}
	/// @param[in] exclude						An array of objects to exclude from being snapped to. @callerOwnsPointed{array}
	/// @return												True on success of initializing the snap.
	//----------------------------------------------------------------------------------------
	Bool Init(BaseDocument* doc, BaseDraw* bd, AtomArray* exclude = nullptr);

	//----------------------------------------------------------------------------------------
	/// Updates the snaps to use the current settings.
	/// @return												@trueIfOtherwiseFalse{successful}
	//----------------------------------------------------------------------------------------
	Bool Update(void);

	//----------------------------------------------------------------------------------------
	/// Snap routine.
	/// @param[in] p									The position to snap to in global space.
	/// @param[out] result						The position to snap to in global space.
	/// @param[in] flags							The flags to limit snapping on selection or to a specific type: @enumerateEnum{SNAPFLAGS}
	/// @return												@formatConstant{true} on success of finding a snap point, @formatConstant{false} if no point was found in range to snap to.
	//----------------------------------------------------------------------------------------
	Bool Snap(const Vector& p, SnapResult& result, SNAPFLAGS flags = SNAPFLAGS::NONE);

	//----------------------------------------------------------------------------------------
	/// Snap intersection routine.
	/// @param[in] p									The position to snap to in global space.
	/// @param[in] n									The normal of the guide being intersected with in global space.
	/// @param[in] plane							@formatConstant{true} if the intersection should be with a plane, @formatConstant{false} if with a ray/line.
	/// @param[out] result						The result of the successful intersection, user owns this.
	/// @param[in] flags							The flags to limit snapping on selection or to a specific type: @enumerateEnum{SNAPFLAGS}
	/// @return												@formatConstant{true} on success of finding an intersection, @formatConstant{false} if no intersection was found in range.
	//----------------------------------------------------------------------------------------
	Bool Intersect(const Vector& p, const Vector& n, Bool plane, SnapResult& result, SNAPFLAGS flags = SNAPFLAGS::NONE);

	/// @}

	/// @name Inferred
	/// @{

	//----------------------------------------------------------------------------------------
	/// Adds an inferred guide point, line or plane to the scene for snapping to.
	/// @param[in] doc								The current document. @callerOwnsPointed{document}
	/// @param[in] mat								The matrix for the point (axis), line, or plane, z points along the line or as the normal for the plane.
	/// @param[in] type								The type of inferred guide to add: @enumerateEnum{INFERREDGUIDETYPE}
	/// @return												@formatConstant{true} on success of adding the guide, @formatConstant{false} for a memory error attempting to do so.
	//----------------------------------------------------------------------------------------
	BaseObject* AddInferred(BaseDocument* doc, const Matrix& mat, INFERREDGUIDETYPE type);

	//----------------------------------------------------------------------------------------
	/// Flushes inferred system guides and inferred elements.
	/// @note Should be called at mouse release.
	/// @return												@trueIfOtherwiseFalse{all inferred guides were removed}
	//----------------------------------------------------------------------------------------
	Bool FlushInferred(void);

	/// @}

	/// @name Miscellaneous
	/// @{

	//----------------------------------------------------------------------------------------
	/// Sets the active object and pass it to snap system if is created from current tool.
	/// @note Important for tool guide.
	/// @param[in] op									The new object created in the tool. @callerOwnsPointed{object}
	//----------------------------------------------------------------------------------------
	void SetToolObject(BaseObject* op);

	//----------------------------------------------------------------------------------------
	/// Passes to the snap a new exclusion list.
	/// @note	Has to be done before a snap call and will stay valid just for the next snap session.\n
	///				This is useful if different exclusion types are needed during the drag without reinitializing the class.
	/// @param[in] snapExcludeList		A pointer to a maxon::BaseArray of exclusion data or @formatConstant{nullptr} to clear internal pointer. @callerOwnsPointed{array}
	//----------------------------------------------------------------------------------------
	void SetCustomExcludeList(maxon::BaseArray<SnapExclude>* snapExcludeList);

	/// @}
};

//----------------------------------------------------------------------------------------
/// Checks if snap is enabled for the document @formatParam{doc} or for a specific @formatParam{mode}.
/// @param[in] doc								The document to check. @callerOwnsPointed{document}
/// @param[in] mode								Optionally set a snap mode instead of checking snap for the document.\n
/// 															For example @ref SNAPMODE_GUIDE. See description\dmodeling.h for snap mode IDs.
/// @return												@trueIfOtherwiseFalse{snap is enabled}
//----------------------------------------------------------------------------------------
Bool IsSnapEnabled(BaseDocument* doc, Int32 mode = NOTOK);

//----------------------------------------------------------------------------------------
/// Sets the snap enabled state for the document @formatParam{doc} or a particular @formatParam{mode}.
/// @param[in] state							The state to set: @formatConstant{true} to enable, @formatConstant{false} to disable.
/// @param[in] doc								The document to set the snap mode state for. @callerOwnsPointed{document}
/// @param[in] mode								Optionally set a snap mode instead of enabling snap for the document.\n
/// 															For example @ref SNAPMODE_GUIDE. See description\dmodeling.h for snap mode IDs.
//----------------------------------------------------------------------------------------
void EnableSnap(Bool state, BaseDocument* doc, Int32 mode = NOTOK);

//----------------------------------------------------------------------------------------
/// Gets the snap settings for the document @formatParam{doc} or a specific @formatParam{mode}.
/// @param[in] doc								The document related snap settings to get. @callerOwnsPointed{document}
/// @param[in] snapmode						Optionally specify a snap mode to get or modify its specific settings instead of the global settings.\n
/// 															For example @ref SNAPMODE_GUIDE. See description\dmodeling.h for snap mode IDs.
/// @return												A copy of the snap settings container. See description\dmodeling.h for more information on the returned container.
//----------------------------------------------------------------------------------------
BaseContainer SnapSettings(BaseDocument* doc, Int32 snapmode = NOTOK);

//----------------------------------------------------------------------------------------
/// Sets the snap settings for the document @formatParam{doc} or a specific @formatParam{snapmode}.
/// @note After changing the snap settings, call SpecialEventAdd() with ID @em 440000118 for the snap system to recognize the change.
/// @param[in] doc								The document to set the snap settings for. @callerOwnsPointed{document}
/// @param[in] bc									The container with the snap settings for the document @formatParam{doc} and/or @formatParam{snapmode}. See description\dmodeling.h for more information on the container.
/// @param[in] snapmode						Optionally specify the snap mode to set the settings for instead of the global settings.\n
/// 															For example @ref SNAPMODE_GUIDE. See description\dmodeling.h for snap mode IDs.
//----------------------------------------------------------------------------------------
void SnapSettings(BaseDocument* doc, const BaseContainer& bc, Int32 snapmode = NOTOK);

//----------------------------------------------------------------------------------------
/// Checks if quantizing is enabled.
/// @param[in] doc								The document to check for. @callerOwnsPointed{document}
/// @return												@trueIfOtherwiseFalse{quantizing is enabled}
//----------------------------------------------------------------------------------------
Bool IsQuantizeEnabled(BaseDocument* doc);

//----------------------------------------------------------------------------------------
/// Gets the quantize step values from @ref QUANTIZE_MOVE, @ref QUANTIZE_SCALE etc.
/// @param[in] doc								The document to get the quantize step values. @callerOwnsPointed{document}
/// @param[in] bd									The view to retrieve the quantize values. Not relevant for other quantize modes (pass @formatConstant{nullptr}).
/// @param[in] quantize_mode			The mode to retrieve the values for, e.g. @ref QUANTIZE_MOVE, @ref QUANTIZE_ROTATE, @ref QUANTIZE_SCALE or @ref QUANTIZE_TEXTURE.
/// @return												The real step value.
//----------------------------------------------------------------------------------------
Float QuantizeStep(BaseDocument* doc, BaseDraw* bd, Int32 quantize_mode);

//----------------------------------------------------------------------------------------
/// Sets the quantize step values for @ref QUANTIZE_MOVE, @ref QUANTIZE_SCALE etc.
/// @param[in] doc								The document to set the quantize step values. @callerOwnsPointed{document}
/// @param[in] bd									The view to set the quantize step values. Not relevant for other quantize modes (pass @formatConstant{nullptr}).
/// @param[in] quantize_mode			The mode to set the values for, e.g. @ref QUANTIZE_MOVE, @ref QUANTIZE_ROTATE, @ref QUANTIZE_SCALE or @ref QUANTIZE_TEXTURE.
/// @param[in] val								The value to set the quantizing step size to.
//----------------------------------------------------------------------------------------
void QuantizeStep(BaseDocument* doc, BaseDraw* bd, Int32 quantize_mode, Float val);

//----------------------------------------------------------------------------------------
/// Retrieves the viewport workplane and orientation.
/// @param[in] bd									The view to get the workplane's matrix from. @callerOwnsPointed{view}
/// @param[out] mg								Assigned the correct workplane matrix. Pass @formatConstant{nullptr} to skip.
/// @param[out] scale							Assigned the correct workplane scale.Pass @formatConstant{nullptr} to skip.
/// @param[out] op								Assigned the workplane object. Pass @formatConstant{nullptr} to skip.
/// @return												@trueIfOtherwiseFalse{successful}
//----------------------------------------------------------------------------------------
Bool GetConstructionPlane(BaseDraw* bd, Matrix* mg, Vector* scale, BaseObject** op);

//----------------------------------------------------------------------------------------
/// Retrieve the workplane object for the document @formatParam{doc}.
/// @param[in] doc								The document to retrieve the workplane from. @callerOwnsPointed{document}
/// @return												The workplane object. @theOwnsPointed{document,object}
//----------------------------------------------------------------------------------------
BaseObject* GetWorkplaneObject(BaseDocument* doc);

//----------------------------------------------------------------------------------------
/// Checks the current workplane locked status for the document @formatParam{doc}.
/// @param[in] doc								The document the workplane belongs to. @callerOwnsPointed{document}
/// @return												@trueIfOtherwiseFalse{the workplane is locked}
//----------------------------------------------------------------------------------------
Bool WorkplaneLock(BaseDocument* doc);

//----------------------------------------------------------------------------------------
/// Sets the current workplane locked status for the document @formatParam{doc}.
/// @param[in] bd									The view to lock the workplane. @callerOwnsPointed{view}
/// @param[in] locked							@trueOtherwiseFalse{to lock the workplane}
//----------------------------------------------------------------------------------------
void WorkplaneLock(BaseDraw* bd, Int32 locked);

//----------------------------------------------------------------------------------------
/// Gets the current workplane matrix for the document @formatParam{doc} and optionally view @formatParam{bd}.
/// @param[in] doc								The document the workplane belongs to. @callerOwnsPointed{document}
/// @param[in] bd									The view to get the workplane's matrix from. If nullptr the locked matrix is returned independently from view. @callerOwnsPointed{view}
/// @return												The workplane matrix.
//----------------------------------------------------------------------------------------
Matrix GetWorkplaneMatrix(BaseDocument* doc, BaseDraw* bd);


//----------------------------------------------------------------------------------------
/// Snap allocator type, used purely in RegisterSnapPlugin().
//----------------------------------------------------------------------------------------
typedef SnapData* SnapDataAllocator (void);

//----------------------------------------------------------------------------------------
/// Registers a snap mode plugin with @C4D.
/// @param[in] id									@uniquePluginID
/// @param[in] str								The name of the plugin.\n
/// 															To affect the order that plugins are displayed in menus add <i>"#$n"</i> as a prefix to this name, where @em n is a number.\n
/// 															Lower numbers are displayed before higher numbers. If name is <i>"--"</i> it will show up as a menu separator.
/// @param[in] help								A small block of information that will appear in the interface to explain how the snap mode operates.
/// @param[in] snapinfo						The registration flags: @enumerateEnum{PLUGINFLAG} @enumerateEnum{PLUGINFLAG_SNAP}
/// @param[in] npalloc						The SnapData allocator. Pass <tt>MySnapMode::Alloc</tt>.
/// @param[in] icon								The icon for the snap mode. The bitmap is copied. \n
/// 															The icon should be of size @em 32x@em 32, but will be scaled if needed.\n
/// 															It must also be @em 24 bits and should if possible include an alpha to support pattern backgrounds.
/// @param[in] priority						The priority of the snap mode, a higher priority will take precedent over other snaps within the radius: @enumerateEnum{SNAPPRIORITY}
/// @param[in] parent_mode				An ID for a parent snap mode, this mode then inherits that modes enabled/disabled state.
/// @return												@trueIfOtherwiseFalse{the snap mode plugin was registered}
//----------------------------------------------------------------------------------------
Bool RegisterSnapPlugin(Int32 id, const maxon::String& str, const maxon::String& help, Int32 snapinfo, SnapDataAllocator* npalloc, BaseBitmap* icon, SNAPPRIORITY priority = SNAPPRIORITY::EDGE, Int32 parent_mode = NOTOK);

/// @}

#endif // C4D_SNAPDATA_H__
