03.04.2026 • C3D Solver

New Geometric Modeling Capabilities: From Constraint Status Checks to Better Dragging in C3D Solver

Ksenia Nosulko, senior math software engineer, C3D Labs, tells about new 3D modeling features in C3D Solver.

C3D Solver manages relationships between geometric objects. It consists of a 2D and 3D solver. They auto-update the geometry as it is edited while maintaining the existing constraints. These constraints can be dimensional (e.g., distances or angles between objects). or geometric: coincidence of points, fixed radii, parallelism, perpendicularity, etc.

The 2D solver now supports a new object: the auxiliary axis. It is defined by a point and a direction vector. The axis is not a curve. Still, like any linear object, it follows any constraints applied to it.

Code example:

// Axis coordinates.
struct GCE_axis
{
  GCE_point origin; // Origin of the axis.
  GCE_vec2d dir;    // Axis direction.  
};
// Register the axis.
     geom_item GCE_AddAxis(GCE_system, const GCE_axis&);
// Constraints applied:
GCE_FIX_GEOM, 			GCE_HORIZONTAL,    
     GCE_VERTICAL, 			GCE_ANGLE_OX, 
     GCE_PERPENDICULAR,		GCE_PARALLEL.
The auxiliary axis offers new capabilities for handling linear (also directional and tangential) dimensions, which can now be specified in any direction (Fig. 1).

New Geometric Modeling Capabilities: From Constraint Status Checks to Better Dragging in C3D Solver, photo 1
Fig. 1. Directional dimensions

To create such a dimension, we define an auxiliary axis, apply a constraint to it (for example, an angle to the OX axis or parallelism), and add the axis to the auxiliary geometry data of the dimensional constraint. The solver guarantees that, once the system of constraints is resolved, the auxiliary axis will be tangent to the curve at the axis origin.

Code example:

// Register the axis. 
geom_item GCE_AddAxis(GCE_system gSys, const GCE_axis&);
// Define the axis direction.
constraint_item GCE_AddUnaryConstraint(GCE_system gSys, constraint_type cType, geom_item axis);
// Linear dimensional constraint parameters.
struct GCE_ldim_pars
{
  GCE_dim_pars dPars; // Value of the dimension.
  geom_item hp[2];    // Auxiliary points or axes.
};
// Apply the distance constraint.
constraint_item GCE_AddDistance(GCE_system gSys, 
				geom_item g[2], const GCE_ldim_pars&);

New Geometric Modeling Capabilities: From Constraint Status Checks to Better Dragging in C3D Solver, photo 2
Fig. 2. Combined linear and angular patterns

We have also enhanced the pattern functionality. Now, combined patterns that apply two relationships to geometric objects are supported. The new type of patterns is a system of constraints that can be used, for example, to create concentric arrays by combining linear and angular patterns (Fig. 2). The algebra behind it is as follows. We apply a series of transformations to the object: i times in one direction, j times in another, resulting in a new instance at the desired location. In this way, two underlying patterns are combined into a composite pattern with a special arrangement of the object instances.

Code example:

// Combined pattern definition.
pattern_item GCE_AddCompositionPattern(GCE_system, pattern_item, pattern_item);
// Creating a combined pattern.
pattern_item pattern1 = GCE_AddAngularPattern(solver, point, angle);
pattern_item pattern2 = GCE_AddLinearPattern(solver, vec);
pattern_item pattern  = GCE_AddCompositionPattern(solver, pattern1, pattern2);
GCE_AddPatterned(solver, pattern, geom1, geom2, 2, 3);

New Geometric Modeling Capabilities: From Constraint Status Checks to Better Dragging in C3D Solver, photo 3
Fig. 3. Mirror pattern

Another significant improvement of the pattern functionality is mirror patterns (Fig. 3). It is particularly useful for creating circular or linear grooves with a symmetrical arrangement of the groove elements relative to a given axis. In such patterns, each instance fully depends on the original object. Instances always follow the original. To define this pattern, we need an axis: a straight line, a segment, or its derivatives (like an equidistant line or a limited curve). Next, we create the pattern object with the original object and its symmetrical instance. We are going to integrate the mirror and the combined patterns. With this, we will be able to create more complex symmetrical structures, such as alternatively shifted copies like footprints in the sand.

Code example:

// Declaration of a mirror pattern relative to a linear object.
// axis is a symmetry axis of one of the following types: GCE_LINE, GCE_LINE_SEGMENT
pattern_item GCE_AddSymmetricPattern(GCE_system, geom_item axis);
// Creating a mirror copy.
geom_item line = GCE_AddLine(solver, lin);
pattern_item patternS = GCE_AddSymmetricPattern(solver, line);
geom_item arc = GCE_AddBoundedCurve(solver, circle, pnt[2]);
geom_item instance = GCE_AddInstance(solver, patternS, arc, 1);

New Geometric Modeling Capabilities: From Constraint Status Checks to Better Dragging in C3D Solver, photo 4
Fig. 4. Editing the spline control point weights

Another new feature in the 2D solver is the new functions that deal with spline control point weights (Fig. 4). The previous weights changing procedure was as follows: the object had to be deleted, its properties changed, and then restored along with its constraints. With the new GCE_SetWeight function, it is now an interactive process available at any moment. This optimization is particularly noticeable when working with patterns, as changes are auto-propagated to the entire family of curves. This not only saves time but also makes spline operations more reliable.

Code example:

// Change the weight of the spline control point.
void GCE_SetWeight(GCE_system, geom_item spline, size_t pntIdx, double weight);
// Restore the weight of the spline control point.
double GCE_WeightOf(GCE_system, geom_item spline, size_t pntIdx);

As for the 3D solver innovations, we focused on better verification that the assembly components are fully constrained. The designer needs to know if all the elements are properly positioned and constrained. The GCM_IsWellDefined function provides such constraint status checks. It helps understand whether the geometric and kinematic definitions are complete. Previously, there was an issue with underconstrained assemblies: undefined elements could affect the status check of fully constrained elements. As a result, the user could lose track of which components needed modifications. We have fixed this. C3D Solver now reliably distinguishes between fully constrained and underconstrained components within an underconstrained assembly. We have also fixed some related errors and optimized the DoF status check procedure.

Code example:

typedef enum
{  
    // The object is fully constrained or frozen. (DOF = 0).
    GCM_DOF_RESULT_WellDefined,
    // The object can be translated or rotated.
    GCM_DOF_RESULT_UnderDefined,  
    // The object DoF is not detected.
    GCM_DOF_RESULT_Unknown       
} GCM_dof_result;
// Is the object fully constrained, or underconstrained?
GCM_dof_result GCM_IsWellDefined(GCM_system, GCM_geom gItem);

Another aspect is the extended DoF analysis. Besides the available constraint status, the user can now access translational degrees of freedom and instantaneous displacement vectors for a geometric object point. The GCM_PointDOF function returns these properties. We are going to return rotational degrees of freedom and their axes to provide even more accurate information about the kinematics of the assembly.

Code example:

// Geometric object DoF details.
struct GCM_dof_record
{
  GCM_dof_result result; // DoF result code.
  // Translational degree of freedom info.
  GCM_dof_type   tdof;
  GCM_vec2d      dir1;
  GCM_vec2d      dir2;
  // Rotational degree of freedom info.
  GCM_dof_type   rdof;
  GCM_point      center;
  GCM_vec2d      axis1;
  GCM_vec2d      axis2;
};
// Check the DoF of the point and possible translations. 
GCM_dof_record GCM_PointDOF(GCM_system, GCM_geom, const MbCartPoint3D& ctrlPnt);

As we updated the DoF check procedures, we also significantly improved the dragging functionality. In particular, the solutions to the task above had a positive effect on the wireframe dragging and also improved the stability of other procedures using similar logic.

Dragging is an interactive process with online calculations involved. Geometric objects are moved as allowed by their degrees of freedom. Dragging responds to user inputs like mouse movements when grabbing and moving a drawing object.

A significant improvement is the new function, which supports on-screen dragging and automatically tracks its start and end for the currently captured object. Previously, the initialization and termination methods had to be explicitly called. Now the function itself detects the start and end of dragging. It also detects when the user captures another object or changes the drag plane and initiates readjustment if necessary. All this provides optimal, reliable model manipulation.

Code example:

// Initialize on-screen drag-and-drop.
GCM_result GCM_PrepereReposition(GCM_system, GCM_geom moveGeom, const MbCartPoint3D&, 					      const MbVector3D&);
// End drag mode.
void GCM_FinishReposition(GCM_system);
/**
 The procedure that controls the drag mode.
 \param[in] cursor is the Current cursor position in the GSC.
 \param[in] zAxis is the normal to the drag plane, specified in the assembly GSC. 
*/
GCM_result GCM_SolveReposition(GCM_system, GCM_geom moveGeom,
			      const MbCartPoint3D& cursor, const MbVector3D& zAxis);

Ksenia Nosulko, Senior math software engineer C3D Labs
Ksenia Nosulko
Senior math software engineer
C3D Labs
Share
Up