kinematic - Kinematic solver/constraint system

This module defines the types and functions for kinematic manimulation and computation.

A Kinematic is a conceptual approach of mechanisms. It sort parts in several groups with the same movement (so in a solid, the solids are all bound together), and it links the defined solids by joints corresponding to the constraints each solid put to the other solids in the joint. That way no matter what are the parts, and what are their shape, even whan surfaces links the solids - the solid always have the same movements when there is the same joints between them.

So to analyse a mechanisme we look at its kinematic. And that can be done prior or after the part design as it is independant.

A kinematic in itself is a set of solids, observing movement relations. Those are modeled across the following classes: Solid and Kinematic.

Solids are considered to be undeformable, this allows the to use the Screw theory to represent the force and movement variables (see In this module, screws are called Screw.


In case of undeformable solids, torsors makes possible to represent both the translative and rotative part of each movement aspect, independently from the point in the solid.

class Screw(resulting=None, momentum=None, position=None)
a 3D torsor aka Screw aka Wrench aka Twist - is a mathematical object defined as follow:
  • a resulting vector R

  • a momentum vector field M

the momentum is a function of space, satisfying the relationship:

M(A) = M(B) + cross(R, A-B)

therefore it is possible to represent a localized torsor such as:
  • R = resulting

  • M = momentum vector at position P

  • P = position at which M takes the current value

torsor are usefull for generalized solid mechanics to handle multiple variables of the same nature:
  • force torsor:

    Screw(force, torque, pos)

  • velocity (aka kinematic) torsor:

    Screw(rotation, velocity, pos)

  • kinetic (inertia) torsor:

    Screw(linear movement quantity, rotational movement quantity, pos)

all these torsors makes it possible to represent all these values independently from expression location







Of course, as any vector variables, Screw implements + - with other Torsor, and * / with float

locate(pt) madcad.kinematic.Screw

gets the same torsor, but expressed for an other location

transform(mat) madcad.kinematic.Screw

changes the torsor from coordinate system

class Solid(pose=None, **content)

Solid for kinematic definition, used as variable by the kinematic solver


rotation from local to world space




displacement from local to world




objects to display using the solid’s pose




optional name to display on the scheme



property pose: glm.dmat4x4

transformation from local to global space, therefore containing the translation and rotation from the global origin


contenient method to set many elements in one call. equivalent to self.content.update(objs)


add an item in self.content, a key is automatically created for it and is returned

transform(trans) madcad.kinematic.Solid

create a new Solid moved by the given transformation, with the same content

place(*args, **kwargs) madcad.kinematic.Solid

strictly equivalent to .transform(placement(...))

class Kinematic(joints, fixed=(), solids=None)

Holds a kinematic definition, and methods to use it The solid objects used are considered as variables and are modified inplace by methods, and can be modified at any time by outer functions The joints are not modified in any case (and must not be modified while a Kinematic is using it)


the joints constraints


all the solids the joint applys on, and eventually more


the root solids that is considered to be fixed to the ground

solvekin(joints, fixed=(), precision=0.0001, maxiter=None, damping=1)

solver for kinematic joint constraints.

Unlike solve, the present solver is dedicated to kinematic usage (and far more efficient and precise). It doesn’t rely on variables as defined by solve, but instead use Solids as constraints.

See the joints module for joints definitions.

makescheme(joints, color=None)

create kinematic schemes and add them as visual elements to the solids the joints applies on

placement(*pairs, precision=0.001)

return a transformation matrix that solved the placement constraints given by the surface pairs

  • pairs – a list of surface pairs to convert to kinematic joints using guessjoint

  • precision – surface guessing and kinematic solving precision (distance)

each pair define a joint between the two assumed solids (a solid for the left members of the pairs, and a solid for the right members of the pairs). placement will return the pose of the first relatively to the second, satisfying the constraints.


>>> # get the transformation for the pose
>>> pose = placement(
...             (screw['part'].group(0), other['part'].group(44)),  # two cylinder surfaces: Gliding joint
...             (screw['part'].group(4), other['part'].group(25)),    # two planar surfaces: Planar joint
...             )  # solve everything to get solid's pose
>>> # apply the transformation to the solid
>>> screw.pose = pose
>>> # or
...             (screw['part'].group(0), other['part'].group(44)),
...             (screw['part'].group(4), other['part'].group(25)),
...             )

suppose we have those parts to assemble and it’s hard to guess the precise pose transform between them


placement gives the pose for the screw to make the selected surfaces coincide

explode(solids, factor=1, offsets=None) -> (solids:list, graph:Mesh)

move the given solids away from each other in the way of an exploded view. makes easier to seen the details of an assembly . See explode_offsets for the algorithm

  • solids – a list of solids (copies of each will be made before displacing)

  • factor – displacement factor, 0 for no displacement, 1 for normal displacement

  • offsets – if given, must be the result of explode_offsets(solids)


>>> # pick some raw model and separate parts
>>> imported = read(folder+'/some_assembly.stl')
>>> imported.mergeclose()
>>> parts = []
>>> for part in imported.islands():
...     part.strippoints()
...     parts.append(Solid(part=segmentation(part)))
>>> # explode the assembly to look into it
>>> exploded = explode(parts)

before operation


after operation

explode_offsets(solids) [solid_index, parent_index, offset, barycenter]

build a graph of connected objects, ready to create an exploded view or any assembly animation. See explode() for an example. The exploded view is computed using the meshes contained in the given solids, so make sure there everything you want in their content.

Complexity is O(m * n) where m = total number of points in all meshes, n = number of solids


Despite the hope that this function will be helpful, it’s (for computational cost reasons) not a perfect algorithm for complex assemblies (the example above is at the limit of a simple one). The current algorithm will work fine for any simple enough assembly but may return unexpected results for more complexe ones.