SODRAGGER(3iv)

πŸ’– SoDragger(3IV) πŸ’–

okay so bestie, you opened SoDragger(3IV) and honestly? iconic. this is the base-class baddie that every click-drag-release nodekit inherits its whole personality from. grab a strawberry πŸ“ and let's go.


πŸŽ€ NAME πŸŽ€

SoDragger β€” base class for nodekits that move in response to click-drag-release mouse events

🌸 INHERITS FROM 🌸

SoBase > SoFieldContainer > SoNode > SoBaseKit > SoInteractionKit > SoDragger

✨ SYNOPSIS ✨

#include <Inventor/draggers/SoDragger.h>

typedef void        SoDraggerCB(void *userData, SoDragger *dragger)

Fields from class SoDragger:

SoSFBool            isActive

Fields from class SoInteractionKit:

SoSFEnum            renderCaching
SoSFEnum            boundingBoxCaching
SoSFEnum            renderCulling
SoSFEnum            pickCulling

Parts from class SoBaseKit:

(SoNodeKitListPart)  callbackList

Methods from class SoDragger:

void                addStartCallback(SoDraggerCB *f, void *userData =
                         NULL)
void                removeStartCallback(SoDraggerCB *f, void *userData =
                         NULL)
void                addMotionCallback(SoDraggerCB *f, void *userData =
                         NULL)
void                removeMotionCallback(SoDraggerCB *f, void *userData =
                         NULL)
void                addFinishCallback(SoDraggerCB *f, void *userData =
                         NULL)
void                removeFinishCallback(SoDraggerCB *f, void *userData =
                         NULL)
void                addValueChangedCallback(SoDraggerCB *f, void *userData
                         = NULL)
void                removeValueChangedCallback(SoDraggerCB *f, void
                         *userData = NULL)
SbBool              enableValueChangedCallbacks()
void                setMinGesture(int pixels)
int                 getMinGesture() const
static void         setMinScale(float newMinScale)
static float        getMinScale()
static const SoNodekitCatalog *
                    getClassNodekitCatalog() const
static SoType       getClassTypeId()

Methods from class SoInteractionKit:

virtual SbBool      setPartAsPath(const SbName &partName, SoPath
                         *surrogatePath )

Methods from class SoBaseKit:

virtual const SoNodekitCatalog *
                         getNodekitCatalog() const
virtual SoNode *         getPart(const SbName &partName, SbBool
                              makeIfNeeded)
SbString                 getPartString(const SoBase *part)
virtual SoNodeKitPath *  createPathToPart(const SbName &partName, SbBool
                              makeIfNeeded, const SoPath *pathToExtend =
                              NULL)
virtual SbBool           setPart(const SbName &partName, SoNode *newPart)
SbBool                   set(char *partName, char *parameters)
SbBool                   set(char *nameValuePairs)
static SbBool            isSearchingChildren()
static void              setSearchingChildren(SbBool newVal)

Methods from class SoNode:

void                setOverride(SbBool state)
SbBool              isOverride() const
SoNode *            copy(SbBool copyConnections = FALSE) const
virtual SbBool      affectsState() const
static SoNode *     getByName(const SbName &name)
static int          getByName(const SbName &name, SoNodeList &list)

Methods from class SoFieldContainer:

void                setToDefaults()
SbBool              hasDefaultValues() const
SbBool              fieldsAreEqual(const SoFieldContainer *fc) const
void                copyFieldValues(const SoFieldContainer *fc, SbBool
                         copyConnections = FALSE)
void                get(SbString &fieldDataString)
virtual int         getFields(SoFieldList &resultList) const
virtual SoField *   getField(const SbName &fieldName) const
SbBool              getFieldName(const SoField *field, SbName &fieldName)
                         const
SbBool              isNotifyEnabled() const
SbBool              enableNotify(SbBool flag)

Methods from class SoBase:

void                ref()
void                unref() const
void                unrefNoDelete() const
void                touch()
virtual SoType      getTypeId() const
SbBool              isOfType(SoType type) const
virtual void        setName(const SbName &name)
virtual SbName      getName() const

Macros from class SoBaseKit:

SO_GET_PART(kit, partName, partClass)
SO_CHECK_PART(kit, partName, partClass)

πŸ¦‹ DESCRIPTION πŸ¦‹

okay so <pink>SoDragger</pink> is THE base class for every nodekit you move around with the mouse β€” the whole click β†’ drag β†’ release` situation. but like, more precisely, because precision is the vibe:

  • start β€” mouse button 1 gets pressed while you're hovering over the dragger, which picks it
  • dragging β€” the mouse motion events come in and the dragger interprets them, and that turns into some kind of motion and/or a change to a field
  • finish β€” mouse goes up

periodt. that's the whole lifecycle. πŸ’…

every dragger has its own lil paradigm for reading mouse motion and changing its fields because of it. draggers map that 2D mouse motion into motion of a point on 3D <purple>lines</purple>, <purple>planes</purple>, <purple>spheres</purple> or <purple>cylinders</purple> β€” (peep the SbProjector reference pages for the lore on that one). then they react to that point moving through 3-space by <mint>scaling</mint>, <mint>translating</mint>, or <mint>rotating</mint>. for example: SoTranslate2Dragger maps mouse motion onto a 3D plane, then translates to follow the cursor as it moves around inside that plane. lowkey elegant fr fr.

every dragger has fields describing its current state, like it's posting a status update. scaling draggers have a scaleFactor field, rotational draggers have a rotation field, etc etc. and literally all of them have the isActive field, which is defined right here in this class. it's TRUE while the dragger is being dragged, FALSE otherwise. simple as. ✨

draggers that only have one part to pick and one motion field are called <yellow>simple draggers. examples: SoRotateDiscDragger, SoScale1Dragger, and SoTranslate2Dragger`. cute, focused, no drama.

draggers that build assemblies out of other draggers and then orchestrate the motion of the whole assembly are called <yellow>composite draggers. SoTransformBoxDraggeris a composite dragger made entirely out of simple draggers.SoDirectionalLightDragger contains both a simple dragger (SoRotateSphericalDragger) and a composite dragger (SoDragPointDragger). when you're using a composite dragger, the fields of the compositedragger are the ones you should be working with β€” that's the main character. the draggers lower down in the assemblage usually have zeroed out values, they're just there to support. for example, when you drag the face of a transformBox, anSoTranslate2Dragger`, the transformBox "steals" the translation from the child dragger and transfers it up to the top of the composite dragger, where it effects all pieces of the assemblage. the way that works?? 🫢

draggers always keep their fields up to date, <rainbow>including while they're being dragged</rainbow>. so you can use field-to-field connections and engines to hook dragger values up to other parts of your scene graph. which means draggers can be utilized as input devices for mouse-driven 3D interface elements, no cap. you can also register value-changed callbacks, and those get called whenever any of the fields changes.

also β€” and this is the part that's giving genius β€” if you set the field of a dragger through some method other than dragging (by calling setValue(), for example), the dragger's internal SoFieldSensor will sense it, and the dragger will <pink>move itself to satisfy that new value</pink>. obsessed.

this makes it easy to constrain draggers so their fields stay inside certain limits: if the limit gets exceeded, you just set it back to the exceeded maximum or minimum. you can do this even while the dragger is in use, by constraining the field value inside a value-changed callback that you add with addValueChangedCallback(). but listen β€” in that case, be sure to <red>temporarily disable the other value-changed callbacks</red> using enableValueChangedCallbacks(). doing that prevents <red>infinite-looping`; changing the value followed by calling the callbacks which change the value ad infinitum. we do NOT want that spiral bestie πŸ’€

⚠️ important vibe check: when you drag a dragger, the dragger only moves itself. draggers do not change the state or affect objects that follow in the scene graph. for example a dragger does not ever behave like an SoTransform and change the current transformation matrix. draggers are not transforms, even if they have field names like translation, rotation, scaleFactor.

many draggers, such as SoTrackballDragger, have a corresponding SoTransformManip β€” in this case SoTrackballManip. the manipulator is a subclass of SoTransform, and it affects other objects in the scene; it uses a trackball dragger to provide its user interface. so draggers get employed extensively by manipulators. callback functions on the dragger let its employer get notified of <mint>start, motion</mint>, <mint>finish, and value changes</mint>. in all cases, the callback function is passed a pointer to the dragger which initiated the callback. (see the various man pages for more details on specific draggers and manipulators).

all draggers are nodekits. however β€” plot twist β€” draggers do not list their parts in the Parts section of the reference page. instead there's a section called <lavender>Dragger Resources, which is way more suited to describing the parts made available to the programmer. and because of space limitations, the Dragger Resources section only shows up in the online` versions of the reference pages. fyi.

every dragger has some parts you can pick on, and other parts that replace them when they're active or moving. those active parts are often just the same geometry in another color. draggers also have pieces for displaying feedback. each of these pieces has a default scene graph, plus a special function within the dragger. each part also has a <purple>resource name. all of this info lives in the DRAGGER RESOURCES` section.

since draggers are nodekits, you can set the parts in any instance of a dragger using setPart().

but draggers also give each part a resource name. when a dragger builds a part, it looks in the global dictionary for the node with that resourceName. by putting a new entry in the dictionary, you can override that default. the default part geometries are defined as resources for each class, and each class has a file you can change to alter the defaults. those files are listed in each dragger's man page. you can make your program use different default resources for the parts by:

  1. copying the listed file from the directory /usr/share/data/draggerDefaults into your own directory,
  2. editing the file,
  3. and then setting the environment variable SO_DRAGGER_DIR to be a path to that directory.

slay. customization era. πŸ‘‘


🌈 FIELDS 🌈

  • SoSFBool isActive β€” TRUE when mouse is down and dragging, else FALSE.

more of this page is coming bestie, don't go anywhere πŸ§šπŸ’œ

✨ METHODS ✨

Okay bestie, these are the functions you actually call. All of them live on the dragger, all of them matter. Let's go one by one πŸ’–

  • void addStartCallback(SoDraggerCB *f, void *userData = NULL)
  • void removeStartCallback(SoDraggerCB *f, void *userData = NULL)

Start callbacks fire after mouse button 1 goes down and the dragger has figured out that it got picked. If it's actually gonna start dragging, it grabs events and invokes the startCallbacks. So like… the moment it commits to the bit πŸ’…

  • void addMotionCallback(SoDraggerCB *f, void *userData = NULL)
  • void removeMotionCallback(SoDraggerCB *f, void *userData = NULL)

Motion callbacks get called after every single movement of the mouse while dragging is happening. Every wiggle. Every scoot. It's giving constant updates ✨

  • void addFinishCallback(SoDraggerCB *f, void *userData = NULL)
  • void removeFinishCallback(SoDraggerCB *f, void *userData = NULL)

Finish callbacks run after dragging ends and the dragger has stopped grabbing events. The grand finale πŸŽ€

  • void addValueChangedCallback(SoDraggerCB *f, void *userData = NULL)
  • void removeValueChangedCallback(SoDraggerCB *f, void *userData = NULL)

Value-changed callbacks happen after a dragger changes any of its fields. Important caveat bestie: this does not include changes to the isActive field. Also peep enableValueChangedCallbacks below πŸ‘€

  • SbBool enableValueChangedCallbacks()

Okay so you can temporarily disable a dragger's valueChangedCallbacks, and this method returns a value telling you whether callbacks were already enabled before you messed with them. Use this if you're writing your own valueChanged callback and you change one of the dragger's fields inside that callback β€” like, say, when you're writing a callback to constrain your dragger. The move is: disable first, then change the field, then re-enable the callbacks (only if they were enabled to start with). All of this exists to stop you from falling into an infinite loop of changing values β†’ calling callbacks β†’ which change values β†’ which call callbacks β†’ forever and ever πŸ˜­πŸ’€

  • void setMinGesture(int pixels)
  • int getMinGesture() const

Set and get the number of pixels of movement required to initiate a constraint gesture. Default is 8. No cap 🫢

  • static void setMinScale(float newMinScale)
  • static float getMinScale()

The smallest scale that any dragger will write. If the user tries to go below this amount, the dragger just sets it to this minimum instead. Default is .001. It's a floor, not a suggestion πŸ’…

  • static const SoNodekitCatalog *getClassNodekitCatalog() const

Returns an SoNodekitCatalog for this class. Yep. That's the whole thing πŸ“¦

  • static SoType getClassTypeId()

Returns the type identifier for this class. Your little ID badge πŸͺͺ


πŸ—‚οΈ CATALOG PARTS πŸ—‚οΈ

Here's the part where we list the parts. Meta, I know. Obsessed.

All parts

  • callbackList β€” Part Type: NodeKitListPart, Default Type: --, NULL by Default: yes

Extra information for list parts from above table

  • callbackList β€” Container Type: Separator, Permissible Types: Callback, EventCallback
So the callbackList part is the only one on the menu, it defaults to NULL, and if you do populate it, it's a Separator that can hold Callback and EventCallback nodes. That's the whole tea β˜•

πŸ“„ FILE FORMAT/DEFAULTS πŸ“„

This is literally what a Dragger looks like when it spawns into the world, no edits, straight out the box:

Dragger {
     renderCaching       AUTO
     boundingBoxCaching  AUTO
     renderCulling       AUTO
     pickCulling         AUTO
     isActive            FALSE
     callbackList        NULL
}

Every single one of those defaults is exactly as written. Don't come for me, I'm just the messenger 🧚


πŸ‘€ SEE ALSO πŸ‘€

Go read these next, bestie, they're all in the family:

  • SoInteractionKit
  • SoCenterballDragger
  • SoDirectionalLightDragger
  • SoDragPointDragger
  • SoHandleBoxDragger
  • SoJackDragger
  • SoPointLightDragger
  • SoRotateCylindricalDragger
  • SoRotateDiscDragger
  • SoRotateSphericalDragger
  • SoScale1Dragger
  • SoScale2Dragger
  • SoScale2UniformDragger
  • SoScaleUniformDragger
  • SoSpotLightDragger
  • SoTabBoxDragger
  • SoTabPlaneDragger
  • SoTrackballDragger
  • SoTransformBoxDragger
  • SoTransformerDragger
  • SoTranslate1Dragger
  • SoTranslate2Dragger

And that's the whole page, periodt. This one came to you from the SoDragger(3IV) manual page β€” version 3IV energy, dated however long ago, and honestly? Iconic. Stay draggin' πŸ’–βœ¨