Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/transform/transform_constraints.c
| Show First 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | |||||
| #include "transform_orientations.h" | #include "transform_orientations.h" | ||||
| #include "transform_snap.h" | #include "transform_snap.h" | ||||
| /* Own include. */ | /* Own include. */ | ||||
| #include "transform_constraints.h" | #include "transform_constraints.h" | ||||
| static void drawObjectConstraint(TransInfo *t); | static void drawObjectConstraint(TransInfo *t); | ||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name Internal Utilities | |||||
| * \{ */ | |||||
| static void projection_matrix_calc(const TransInfo *t, float r_pmtx[3][3]) | static void projection_matrix_calc(const TransInfo *t, float r_pmtx[3][3]) | ||||
| { | { | ||||
| unit_m3(r_pmtx); | unit_m3(r_pmtx); | ||||
| if (!(t->con.mode & CON_AXIS0)) { | if (!(t->con.mode & CON_AXIS0)) { | ||||
| zero_v3(r_pmtx[0]); | zero_v3(r_pmtx[0]); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 306 Lines • ▼ Show 20 Lines | static void planeProjection(const TransInfo *t, const float in[3], float out[3]) | ||||
| factor = dot_v3v3(vec, vec) / factor; | factor = dot_v3v3(vec, vec) / factor; | ||||
| copy_v3_v3(vec, norm); | copy_v3_v3(vec, norm); | ||||
| mul_v3_fl(vec, factor); | mul_v3_fl(vec, factor); | ||||
| add_v3_v3v3(out, in, vec); | add_v3_v3v3(out, in, vec); | ||||
| } | } | ||||
| /* | /** | ||||
| * Generic callback for constant spatial constraints applied to linear motion | * Generic callback for constant spatial constraints applied to linear motion | ||||
| * | * | ||||
| * The IN vector in projected into the constrained space and then further | * The `in` vector in projected into the constrained space and then further | ||||
| * projected along the view vector. | * projected along the view vector. | ||||
| * (in perspective mode, the view vector is relative to the position on screen) | * (in perspective mode, the view vector is relative to the position on screen) | ||||
| */ | */ | ||||
| static void applyAxisConstraintVec( | static void applyAxisConstraintVec( | ||||
| TransInfo *t, TransDataContainer *UNUSED(tc), TransData *td, const float in[3], float out[3]) | TransInfo *t, TransDataContainer *UNUSED(tc), TransData *td, const float in[3], float out[3]) | ||||
| { | { | ||||
| copy_v3_v3(out, in); | copy_v3_v3(out, in); | ||||
| if (!td && t->con.mode & CON_APPLY) { | if (!td && t->con.mode & CON_APPLY) { | ||||
| bool is_snap_to_point = false, is_snap_to_edge = false, is_snap_to_face = false; | bool is_snap_to_point = false, is_snap_to_edge = false, is_snap_to_face = false; | ||||
| mul_m3_v3(t->con.pmtx, out); | mul_m3_v3(t->con.pmtx, out); | ||||
| ▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | if (!is_snap_to_point || is_snap_to_edge || is_snap_to_face) { | ||||
| /* View alignment correction. */ | /* View alignment correction. */ | ||||
| axisProjection(t, c, in, out); | axisProjection(t, c, in, out); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* | /** | ||||
| * Generic callback for object based spatial constraints applied to linear motion | * Generic callback for object based spatial constraints applied to linear motion | ||||
| * | * | ||||
| * At first, the following is applied without orientation | * At first, the following is applied without orientation | ||||
| * The IN vector in projected into the constrained space and then further | * The IN vector in projected into the constrained space and then further | ||||
| * projected along the view vector. | * projected along the view vector. | ||||
| * (in perspective mode, the view vector is relative to the position on screen) | * (in perspective mode, the view vector is relative to the position on screen). | ||||
| * | * | ||||
| * Further down, that vector is mapped to each data's space. | * Further down, that vector is mapped to each data's space. | ||||
| */ | */ | ||||
| static void applyObjectConstraintVec( | static void applyObjectConstraintVec( | ||||
| TransInfo *t, TransDataContainer *tc, TransData *td, const float in[3], float out[3]) | TransInfo *t, TransDataContainer *tc, TransData *td, const float in[3], float out[3]) | ||||
| { | { | ||||
| if (!td) { | if (!td) { | ||||
| applyAxisConstraintVec(t, tc, td, in, out); | applyAxisConstraintVec(t, tc, td, in, out); | ||||
| } | } | ||||
| else { | else { | ||||
| /* Specific TransData's space. */ | /* Specific TransData's space. */ | ||||
| copy_v3_v3(out, in); | copy_v3_v3(out, in); | ||||
| if (t->con.mode & CON_APPLY) { | if (t->con.mode & CON_APPLY) { | ||||
| mul_m3_v3(t->spacemtx_inv, out); | mul_m3_v3(t->spacemtx_inv, out); | ||||
| mul_m3_v3(td->axismtx, out); | mul_m3_v3(td->axismtx, out); | ||||
| if (t->flag & T_EDIT) { | if (t->flag & T_EDIT) { | ||||
| mul_m3_v3(tc->mat3_unit, out); | mul_m3_v3(tc->mat3_unit, out); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* | /** | ||||
| * Generic callback for constant spatial constraints applied to resize motion | * Generic callback for constant spatial constraints applied to resize motion. | ||||
| */ | */ | ||||
| static void applyAxisConstraintSize(TransInfo *t, | static void applyAxisConstraintSize(TransInfo *t, | ||||
| TransDataContainer *UNUSED(tc), | TransDataContainer *UNUSED(tc), | ||||
| TransData *td, | TransData *td, | ||||
| float smat[3][3]) | float smat[3][3]) | ||||
| { | { | ||||
| if (!td && t->con.mode & CON_APPLY) { | if (!td && t->con.mode & CON_APPLY) { | ||||
| float tmat[3][3]; | float tmat[3][3]; | ||||
| if (!(t->con.mode & CON_AXIS0)) { | if (!(t->con.mode & CON_AXIS0)) { | ||||
| smat[0][0] = 1.0f; | smat[0][0] = 1.0f; | ||||
| } | } | ||||
| if (!(t->con.mode & CON_AXIS1)) { | if (!(t->con.mode & CON_AXIS1)) { | ||||
| smat[1][1] = 1.0f; | smat[1][1] = 1.0f; | ||||
| } | } | ||||
| if (!(t->con.mode & CON_AXIS2)) { | if (!(t->con.mode & CON_AXIS2)) { | ||||
| smat[2][2] = 1.0f; | smat[2][2] = 1.0f; | ||||
| } | } | ||||
| mul_m3_m3m3(tmat, smat, t->spacemtx_inv); | mul_m3_m3m3(tmat, smat, t->spacemtx_inv); | ||||
| mul_m3_m3m3(smat, t->spacemtx, tmat); | mul_m3_m3m3(smat, t->spacemtx, tmat); | ||||
| } | } | ||||
| } | } | ||||
| /* | /** | ||||
| * Callback for object based spatial constraints applied to resize motion | * Callback for object based spatial constraints applied to resize motion. | ||||
| */ | */ | ||||
| static void applyObjectConstraintSize(TransInfo *t, | static void applyObjectConstraintSize(TransInfo *t, | ||||
| TransDataContainer *tc, | TransDataContainer *tc, | ||||
| TransData *td, | TransData *td, | ||||
| float smat[3][3]) | float smat[3][3]) | ||||
| { | { | ||||
| if (td && t->con.mode & CON_APPLY) { | if (td && t->con.mode & CON_APPLY) { | ||||
| float tmat[3][3]; | float tmat[3][3]; | ||||
| float imat[3][3]; | float imat[3][3]; | ||||
| ▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | if (r_angle && (mode & CON_NOFLIP) == 0 && hasNumInput(&t->num) == 0) { | ||||
| float view_vector[3]; | float view_vector[3]; | ||||
| view_vector_calc(t, t->center_global, view_vector); | view_vector_calc(t, t->center_global, view_vector); | ||||
| if (dot_v3v3(r_vec, view_vector) > 0.0f) { | if (dot_v3v3(r_vec, view_vector) > 0.0f) { | ||||
| *r_angle = -(*r_angle); | *r_angle = -(*r_angle); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* | /** | ||||
| * Generic callback for constant spatial constraints applied to rotations | * Generic callback for constant spatial constraints applied to rotations | ||||
| * | * | ||||
| * The rotation axis is copied into VEC. | * The rotation axis is copied into `vec`. | ||||
| * | * | ||||
| * In the case of single axis constraints, the rotation axis is directly the one constrained to. | * In the case of single axis constraints, the rotation axis is directly the one constrained to. | ||||
| * For planar constraints (2 axis), the rotation axis is the normal of the plane. | * For planar constraints (2 axis), the rotation axis is the normal of the plane. | ||||
| * | * | ||||
| * The following only applies when CON_NOFLIP is not set. | * The following only applies when #CON_NOFLIP is not set. | ||||
| * The vector is then modified to always point away from the screen (in global space) | * The vector is then modified to always point away from the screen (in global space) | ||||
| * This insures that the rotation is always logically following the mouse. | * This insures that the rotation is always logically following the mouse. | ||||
| * (ie: not doing counterclockwise rotations when the mouse moves clockwise). | * (ie: not doing counterclockwise rotations when the mouse moves clockwise). | ||||
| */ | */ | ||||
| static void applyAxisConstraintRot( | static void applyAxisConstraintRot( | ||||
| TransInfo *t, TransDataContainer *UNUSED(tc), TransData *td, float vec[3], float *angle) | TransInfo *t, TransDataContainer *UNUSED(tc), TransData *td, float vec[3], float *angle) | ||||
| { | { | ||||
| if (!td && t->con.mode & CON_APPLY) { | if (!td && t->con.mode & CON_APPLY) { | ||||
| constraints_rotation_imp(t, t->spacemtx, vec, angle); | constraints_rotation_imp(t, t->spacemtx, vec, angle); | ||||
| } | } | ||||
| } | } | ||||
| /* | /** | ||||
| * Callback for object based spatial constraints applied to rotations | * Callback for object based spatial constraints applied to rotations | ||||
| * | * | ||||
| * The rotation axis is copied into VEC. | * The rotation axis is copied into `vec`. | ||||
| * | * | ||||
| * In the case of single axis constraints, the rotation axis is directly the one constrained to. | * In the case of single axis constraints, the rotation axis is directly the one constrained to. | ||||
| * For planar constraints (2 axis), the rotation axis is the normal of the plane. | * For planar constraints (2 axis), the rotation axis is the normal of the plane. | ||||
| * | * | ||||
| * The following only applies when CON_NOFLIP is not set. | * The following only applies when #CON_NOFLIP is not set. | ||||
| * The vector is then modified to always point away from the screen (in global space) | * The vector is then modified to always point away from the screen (in global space) | ||||
| * This insures that the rotation is always logically following the mouse. | * This insures that the rotation is always logically following the mouse. | ||||
| * (ie: not doing counterclockwise rotations when the mouse moves clockwise). | * (ie: not doing counterclockwise rotations when the mouse moves clockwise). | ||||
| */ | */ | ||||
| static void applyObjectConstraintRot( | static void applyObjectConstraintRot( | ||||
| TransInfo *t, TransDataContainer *tc, TransData *td, float vec[3], float *angle) | TransInfo *t, TransDataContainer *tc, TransData *td, float vec[3], float *angle) | ||||
| { | { | ||||
| if (t->con.mode & CON_APPLY) { | if (t->con.mode & CON_APPLY) { | ||||
| Show All 14 Lines | if (t->con.mode & CON_APPLY) { | ||||
| else { | else { | ||||
| axismtx = td->axismtx; | axismtx = td->axismtx; | ||||
| } | } | ||||
| constraints_rotation_imp(t, axismtx, vec, angle); | constraints_rotation_imp(t, axismtx, vec, angle); | ||||
| } | } | ||||
| } | } | ||||
| /*--------------------- INTERNAL SETUP CALLS ------------------*/ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name Internal Setup Calls | |||||
| * \{ */ | |||||
| void setConstraint(TransInfo *t, int mode, const char text[]) | void setConstraint(TransInfo *t, int mode, const char text[]) | ||||
| { | { | ||||
| BLI_strncpy(t->con.text + 1, text, sizeof(t->con.text) - 1); | BLI_strncpy(t->con.text + 1, text, sizeof(t->con.text) - 1); | ||||
| t->con.mode = mode; | t->con.mode = mode; | ||||
| projection_matrix_calc(t, t->con.pmtx); | projection_matrix_calc(t, t->con.pmtx); | ||||
| startConstraint(t); | startConstraint(t); | ||||
| Show All 18 Lines | void setAxisMatrixConstraint(TransInfo *t, int mode, const char text[]) | ||||
| t->con.applyVec = applyObjectConstraintVec; | t->con.applyVec = applyObjectConstraintVec; | ||||
| t->con.applySize = applyObjectConstraintSize; | t->con.applySize = applyObjectConstraintSize; | ||||
| t->con.applyRot = applyObjectConstraintRot; | t->con.applyRot = applyObjectConstraintRot; | ||||
| t->redraw = TREDRAW_HARD; | t->redraw = TREDRAW_HARD; | ||||
| } | } | ||||
| void setLocalConstraint(TransInfo *t, int mode, const char text[]) | void setLocalConstraint(TransInfo *t, int mode, const char text[]) | ||||
| { | { | ||||
| if (t->flag & T_EDIT) { | if ((t->flag & T_EDIT) || t->data_len_all == 1) { | ||||
| /* Although in edit-mode each object has its local space, use the | /* Although in edit-mode each object has its local space, use the | ||||
| * orientation of the active object. */ | * orientation of the active object. */ | ||||
| setConstraint(t, mode, text); | setConstraint(t, mode, text); | ||||
| } | } | ||||
| else { | else { | ||||
| setAxisMatrixConstraint(t, mode, text); | setAxisMatrixConstraint(t, mode, text); | ||||
| } | } | ||||
| } | } | ||||
| /* | /** | ||||
| * Set the constraint according to the user defined orientation | * Set the constraint according to the user defined orientation | ||||
| * | * | ||||
| * ftext is a format string passed to BLI_snprintf. It will add the name of | * `ftext` is a format string passed to #BLI_snprintf. It will add the name of | ||||
| * the orientation where %s is (logically). | * the orientation where %s is (logically). | ||||
| */ | */ | ||||
| void setUserConstraint(TransInfo *t, int mode, const char ftext[]) | void setUserConstraint(TransInfo *t, int mode, const char ftext[]) | ||||
| { | { | ||||
| char text[256]; | char text[256]; | ||||
| short orientation = t->orient[t->orient_curr].type; | short orientation = t->orient[t->orient_curr].type; | ||||
| if (orientation == V3D_ORIENT_CUSTOM_MATRIX) { | if (orientation == V3D_ORIENT_CUSTOM_MATRIX) { | ||||
| /* Use the real value of the "orient_type". */ | /* Use the real value of the "orient_type". */ | ||||
| orientation = t->orient[0].type; | orientation = t->orient[0].type; | ||||
| } | } | ||||
| const char *spacename = transform_orientations_spacename_get(t, orientation); | const char *spacename = transform_orientations_spacename_get(t, orientation); | ||||
| BLI_snprintf(text, sizeof(text), ftext, spacename); | BLI_snprintf(text, sizeof(text), ftext, spacename); | ||||
| if (t->modifiers & (MOD_CONSTRAINT_SELECT | MOD_CONSTRAINT_PLANE)) { | |||||
| /* Force the orientation of the active object. | |||||
| * Although possible, it is not convenient to use the local or axis constraint | |||||
| * with the modifier to select constraint. | |||||
| * This also follows the convention of older versions. */ | |||||
| setConstraint(t, mode, text); | |||||
| } | |||||
| else { | |||||
| switch (orientation) { | switch (orientation) { | ||||
| case V3D_ORIENT_LOCAL: | case V3D_ORIENT_LOCAL: | ||||
| setLocalConstraint(t, mode, text); | setLocalConstraint(t, mode, text); | ||||
| break; | break; | ||||
| case V3D_ORIENT_NORMAL: | case V3D_ORIENT_NORMAL: | ||||
| if (checkUseAxisMatrix(t)) { | if (checkUseAxisMatrix(t)) { | ||||
| setAxisMatrixConstraint(t, mode, text); | setAxisMatrixConstraint(t, mode, text); | ||||
| break; | break; | ||||
| } | } | ||||
| ATTR_FALLTHROUGH; | ATTR_FALLTHROUGH; | ||||
| case V3D_ORIENT_GLOBAL: | case V3D_ORIENT_GLOBAL: | ||||
| case V3D_ORIENT_VIEW: | case V3D_ORIENT_VIEW: | ||||
| case V3D_ORIENT_CURSOR: | case V3D_ORIENT_CURSOR: | ||||
| case V3D_ORIENT_GIMBAL: | case V3D_ORIENT_GIMBAL: | ||||
| case V3D_ORIENT_CUSTOM_MATRIX: | case V3D_ORIENT_CUSTOM_MATRIX: | ||||
| case V3D_ORIENT_CUSTOM: | case V3D_ORIENT_CUSTOM: | ||||
| default: { | default: { | ||||
| setConstraint(t, mode, text); | setConstraint(t, mode, text); | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| t->con.mode |= CON_USER; | t->con.mode |= CON_USER; | ||||
| } | } | ||||
| /*----------------- DRAWING CONSTRAINTS -------------------*/ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name Drawing Constraints | |||||
| * \{ */ | |||||
| void drawConstraint(TransInfo *t) | void drawConstraint(TransInfo *t) | ||||
| { | { | ||||
| TransCon *tc = &(t->con); | TransCon *tc = &(t->con); | ||||
| if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE, SPACE_NODE)) { | if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE, SPACE_NODE)) { | ||||
| return; | return; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 184 Lines • ▼ Show 20 Lines | for (int i = 0; i < tc->data_len; i++, td++) { | ||||
| if (t->con.mode & CON_AXIS2) { | if (t->con.mode & CON_AXIS2) { | ||||
| drawLine(t, co, axismtx[2], 'Z', options); | drawLine(t, co, axismtx[2], 'Z', options); | ||||
| } | } | ||||
| options &= ~DRAWLIGHT; | options &= ~DRAWLIGHT; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /*--------------------- START / STOP CONSTRAINTS ---------------------- */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name Start / Stop Constraints | |||||
| * \{ */ | |||||
| void startConstraint(TransInfo *t) | void startConstraint(TransInfo *t) | ||||
| { | { | ||||
| t->con.mode |= CON_APPLY; | t->con.mode |= CON_APPLY; | ||||
| *t->con.text = ' '; | *t->con.text = ' '; | ||||
| t->num.idx_max = min_ii(getConstraintSpaceDimension(t) - 1, t->idx_max); | t->num.idx_max = min_ii(getConstraintSpaceDimension(t) - 1, t->idx_max); | ||||
| } | } | ||||
| void stopConstraint(TransInfo *t) | void stopConstraint(TransInfo *t) | ||||
| { | { | ||||
| if (t->orient_curr != 0) { | if (t->orient_curr != 0) { | ||||
| t->orient_curr = 0; | t->orient_curr = 0; | ||||
| transform_orientations_current_set(t, t->orient_curr); | transform_orientations_current_set(t, t->orient_curr); | ||||
| } | } | ||||
| t->con.mode &= ~(CON_APPLY | CON_SELECT); | t->con.mode &= ~(CON_APPLY | CON_SELECT); | ||||
| *t->con.text = '\0'; | *t->con.text = '\0'; | ||||
| t->num.idx_max = t->idx_max; | t->num.idx_max = t->idx_max; | ||||
| } | } | ||||
| /*------------------------- MMB Select -------------------------------*/ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name Middle Mouse Button Select | |||||
| * \{ */ | |||||
| void initSelectConstraint(TransInfo *t) | void initSelectConstraint(TransInfo *t) | ||||
| { | { | ||||
| if (t->orient_curr == 0) { | if (t->orient_curr == 0) { | ||||
| transform_orientations_current_set(t, 1); | transform_orientations_current_set(t, 1); | ||||
| } | } | ||||
| setUserConstraint(t, CON_APPLY | CON_SELECT, "%s"); | setUserConstraint(t, CON_APPLY | CON_SELECT, "%s"); | ||||
| ▲ Show 20 Lines • Show All 130 Lines • ▼ Show 20 Lines | void setNearestAxis(TransInfo *t) | ||||
| else { | else { | ||||
| /* assume that this means a 2D-Editor */ | /* assume that this means a 2D-Editor */ | ||||
| setNearestAxis2d(t); | setNearestAxis2d(t); | ||||
| } | } | ||||
| projection_matrix_calc(t, t->con.pmtx); | projection_matrix_calc(t, t->con.pmtx); | ||||
| } | } | ||||
| /*-------------- HELPER FUNCTIONS ----------------*/ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name Helper Functions | |||||
| * \{ */ | |||||
| int constraintModeToIndex(const TransInfo *t) | int constraintModeToIndex(const TransInfo *t) | ||||
| { | { | ||||
| if ((t->con.mode & CON_APPLY) == 0) { | if ((t->con.mode & CON_APPLY) == 0) { | ||||
| return -1; | return -1; | ||||
| } | } | ||||
| switch (t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2)) { | switch (t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2)) { | ||||
| case (CON_AXIS0): | case (CON_AXIS0): | ||||
| Show All 24 Lines | bool isLockConstraint(TransInfo *t) | ||||
| if ((mode & (CON_AXIS0 | CON_AXIS2)) == (CON_AXIS0 | CON_AXIS2)) { | if ((mode & (CON_AXIS0 | CON_AXIS2)) == (CON_AXIS0 | CON_AXIS2)) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| /* | /** | ||||
| * Returns the dimension of the constraint space. | * Returns the dimension of the constraint space. | ||||
| * | * | ||||
| * For that reason, the flags always needs to be set to properly evaluate here, | * For that reason, the flags always needs to be set to properly evaluate here, | ||||
| * even if they aren't actually used in the callback function. (Which could happen | * even if they aren't actually used in the callback function. | ||||
| * for weird constraints not yet designed. Along a path for example.) | * (Which could happen for weird constraints not yet designed. Along a path for example.) | ||||
| */ | */ | ||||
| int getConstraintSpaceDimension(TransInfo *t) | int getConstraintSpaceDimension(TransInfo *t) | ||||
| { | { | ||||
| int n = 0; | int n = 0; | ||||
| if (t->con.mode & CON_AXIS0) { | if (t->con.mode & CON_AXIS0) { | ||||
| n++; | n++; | ||||
| } | } | ||||
| if (t->con.mode & CON_AXIS1) { | if (t->con.mode & CON_AXIS1) { | ||||
| n++; | n++; | ||||
| } | } | ||||
| if (t->con.mode & CON_AXIS2) { | if (t->con.mode & CON_AXIS2) { | ||||
| n++; | n++; | ||||
| } | } | ||||
| return n; | return n; | ||||
| /* | /* Someone willing to do it cryptically could do the following instead: | ||||
| * Someone willing to do it cryptically could do the following instead: | |||||
| * | * | ||||
| * return t->con & (CON_AXIS0|CON_AXIS1|CON_AXIS2); | * `return t->con & (CON_AXIS0|CON_AXIS1|CON_AXIS2);` | ||||
| * | * | ||||
| * Based on the assumptions that the axis flags are one after the other and start at 1 | * Based on the assumptions that the axis flags are one after the other and start at 1 | ||||
| */ | */ | ||||
| } | } | ||||
| /** \} */ | |||||