src/compas_3gs/algorithms/reciprocation.py
def volmesh_reciprocate(volmesh,
                        formdiagram,

                        kmax=100,
                        weight=1.0,

                        fix_vkeys=[],

                        edge_min=0.1,
                        edge_max=10000,

                        tolerance=1.0,
                        tolerance_boundary=1.0,

                        callback=None,
                        callback_args=None,

                        print_result_info=False):
    """Perpendicularizes the faces of the polyhedral force diagram to the corresponding dual edges in the polyhedral form diagram.

    Parameters
    ----------
    volmesh : VolMesh
        A volmesh object representing a polyhedral force diagram.
    formdiagram : VolMesh or Network
        A network object representing a polyhedral form diagram.
    kmax : int, optional [100]
        Maximum number of iterations.
    weight : float, optional [1.0]
        A float, between 0 and 1, which determines how much each diagram changes. 1 changes the form diagram only, while 0 changes the force diagram only. Default is ``1.0``.
    fix_vkeys : list, optional []
        List of vkeys to fix.
    edge_min : float, optional [None]
        Value for minimum edge length to be imposed.
    edge_max : float, optional [None]
        Value for maximum edge length allowed.
    tolerance: float, optional [0.001]
        Value for convergence tolerance. Deviation is measured by the the dot product of the input and target vectors.
    callback : callable, optional [None]
        A user-defined callback function to be executed after every iteration.
    callback_args : tuple, optional [None]
        Additional parameters to be passed to the callback.
    print_result_info : bool, optional
        If True, print the result of the algorithm.

    Raises
    ------
    Exception
        If a callback is provided, but it is not callable.

    Notes
    -----
    The orientations of the boundary faces of the polyhedral force diagram are always fixed by default.

    .. seealso ::
        compas.geometry.network_parallelise_edges

    """

    if callback:
        if not callable(callback):
            raise Exception('Callback is not callable.')

    free_vkeys = list(set(formdiagram.vertices()) - set(fix_vkeys))

    init_normals = {fkey: volmesh.halfface_normal(fkey) for fkey in volmesh.faces()}

    boundary_fkeys = volmesh.halffaces_on_boundaries()

    # --------------------------------------------------------------------------
    #   1. compute target vectors
    # --------------------------------------------------------------------------
    target_vectors = {}
    target_normals = {}

    for u, v in formdiagram.edges():
        u_hfkey = volmesh.cell_pair_halffaces(u, v)[0]
        face_normal = scale_vector(volmesh.halfface_normal(u_hfkey), weight)
        edge_vector = scale_vector(formdiagram.edge_vector(u, v), 1 - weight)
        target = normalize_vector(add_vectors(face_normal, edge_vector))
        target_vectors[(u, v)] = {'fkey': u_hfkey,
                                  'target': target}
        target_normals[u_hfkey] = target

    for fkey in boundary_fkeys:
        target_normals[fkey] = init_normals[fkey]

    # --------------------------------------------------------------------------
    #   2. loop
    # --------------------------------------------------------------------------
    for k in range(kmax):

        deviation_boundary_perp = 0

        # ----------------------------------------------------------------------
        #   3. update form diagram
        # ----------------------------------------------------------------------
        if weight != 0:

            new_form_xyz = {vkey: [] for vkey in formdiagram.vertices()}

            for u, v in target_vectors:
                target_v = target_vectors[(u, v)]['target']
                edge_v = formdiagram.edge_vector(u, v, unitized=False)

                # target edge length -------------------------------------------
                l = length_vector(edge_v)

                l_min = formdiagram.edge_attribute((u, v), 'l_min')
                l_max = formdiagram.edge_attribute((u, v), 'l_max')

                # min edge
                if edge_min:
                    l_min = edge_min
                if l < l_min:
                    l = l_min

                # max edge
                if edge_max:
                    l_max = edge_max
                if l > l_max:
                    l = l_max

                # check edge orientation ---------------------------------------
                direction = _get_lambda(edge_v, target_v)
                l *= direction

                # collect new coordinates --------------------------------------
                if u in free_vkeys:
                    new_u_xyz = add_vectors(formdiagram.vertex_coordinates(v), scale_vector(target_v, -1 * l))
                    new_form_xyz[u].append(new_u_xyz)

                if v in free_vkeys:
                    new_v_xyz = add_vectors(formdiagram.vertex_coordinates(u), scale_vector(target_v, l))
                    new_form_xyz[v].append(new_v_xyz)

            # compute new vertex coordinates -----------------------------------
            for vkey in free_vkeys:
                final_xyz = centroid_points(new_form_xyz[vkey])
                formdiagram.vertex_update_xyz(vkey, final_xyz)

        # ----------------------------------------------------------------------
        #   4. update force diagram
        # ----------------------------------------------------------------------

        if weight != 1:
            volmesh_planarise(volmesh,
                              kmax=1,
                              target_normals=target_normals)

        # boundary perpness
        for fkey in boundary_fkeys:
            f_normal = volmesh.halfface_normal(fkey)
            target_normal = target_normals[fkey]
            b_perpness = angle_vectors(f_normal, target_normal, deg=True)
            # 1 - abs(dot_vectors(f_normal, target_normal))

            if b_perpness > deviation_boundary_perp:
                deviation_boundary_perp = b_perpness

        # ----------------------------------------------------------------------
        #   5. check convergence
        # ----------------------------------------------------------------------
        perpness = _check_deviation(volmesh, formdiagram)

        if perpness < tolerance and deviation_boundary_perp < tolerance_boundary:

            if print_result_info:
                print_result('Reciprocation', k, perpness)

            break

        # callback / conduit ---------------------------------------------------
        if callback:
            callback(volmesh, formdiagram, k, callback_args)
Perpendicularizes the faces of the polyhedral force diagram to the corresponding dual edges in the polyhedral form diagram.

    Parameters
    ----------
    volmesh : VolMesh
        A volmesh object representing a polyhedral force diagram.
    formdiagram : VolMesh or Network
        A network object representing a polyhedral form diagram.
    kmax : int, optional [100]
        Maximum number of iterations.
    weight : float, optional [1.0]
        A float, between 0 and 1, which determines how much each diagram changes. 1 changes the form diagram only, while 0 changes the force diagram only. Default is ``1.0``.
    fix_vkeys : list, optional []
        List of vkeys to fix.
    edge_min : float, optional [None]
        Value for minimum edge length to be imposed.
    edge_max : float, optional [None]
        Value for maximum edge length allowed.
    tolerance: float, optional [0.001]
        Value for convergence tolerance. Deviation is measured by the the dot product of the input and target vectors.
    callback : callable, optional [None]
        A user-defined callback function to be executed after every iteration.
    callback_args : tuple, optional [None]
        Additional parameters to be passed to the callback.
    print_result_info : bool, optional
        If True, print the result of the algorithm.

    Raises
    ------
    Exception
        If a callback is provided, but it is not callable.

    Notes
    -----
    The orientations of the boundary faces of the polyhedral force diagram are always fixed by default.

    .. seealso ::
        compas.geometry.network_parallelise_edges

    
Perpendicularizes the faces of the polyhedral force diagram to the corresponding dual edges in the polyhedral form diagram.
Parameters

volmesh : VolMesh
A volmesh object representing a polyhedral force diagram.
formdiagram : VolMesh or Network
A network object representing a polyhedral form diagram.
kmax : int, optional [100]
Maximum number of iterations.
weight : float, optional [1.0]
A float, between 0 and 1, which determines how much each diagram changes. 1 changes the form diagram only, while 0 changes the force diagram only.

Raises

Exception
If a callback is provided, but it is not callable.

Notes

The orientations of the boundary faces of the polyhedral force diagram are always fixed by default.


Perpendicularizes the faces of the polyhedral force diagram to the corresponding dual edges in the polyhedral form diagram . Parameters volmesh : VolMesh A volmesh object representing a polyhedral force diagram . formdiagram : VolMesh or Network A network object representing a polyhedral form diagram . kmax : int optional [ 100 ] Maximum number of iterations . weight : float optional [ 1 . 0 ] A float between 0 and 1 which determines how much each diagram changes . 1 changes the form diagram only while 0 changes the force diagram only . Raises Exception If a callback is provided but it is not callable . Notes The orientations of the boundary faces of the polyhedral force diagram are always fixed by default .
def volmesh_reciprocate(volmesh,
                        formdiagram,
                        kmax=100,
                        weight=1.0,
                        fix_vkeys=[],
                        edge_min=0.1,
                        edge_max=10000,
                        tolerance=1.0,
                        tolerance_boundary=1.0,
                        callback=None,
                        callback_args=None,
                        print_result_info=False):
    if callback:
        if not callable(callback):
            raise Exception('Callback is not callable.')
    free_vkeys = list(set(formdiagram.vertices()) - set(fix_vkeys))
    init_normals = {fkey: volmesh.halfface_normal(fkey) for fkey in volmesh.faces()}
    boundary_fkeys = volmesh.halffaces_on_boundaries()
    target_vectors = {}
    target_normals = {}
    for u, v in formdiagram.edges():
        u_hfkey = volmesh.cell_pair_halffaces(u, v)[0]
        face_normal = scale_vector(volmesh.halfface_normal(u_hfkey), weight)
        edge_vector = scale_vector(formdiagram.edge_vector(u, v), 1 - weight)
        target = normalize_vector(add_vectors(face_normal, edge_vector))
        target_vectors[(u, v)] = {'fkey': u_hfkey,
                                  'target': target}
        target_normals[u_hfkey] = target
    for fkey in boundary_fkeys:
        target_normals[fkey] = init_normals[fkey]
    for k in range(kmax):
        deviation_boundary_perp = 0
        if weight != 0:
            new_form_xyz = {vkey: [] for vkey in formdiagram.vertices()}
            for u, v in target_vectors:
                target_v = target_vectors[(u, v)]['target']
                edge_v = formdiagram.edge_vector(u, v, unitized=False)
                l = length_vector(edge_v)
                l_min = formdiagram.edge_attribute((u, v), 'l_min')
                l_max = formdiagram.edge_attribute((u, v), 'l_max')
                if edge_min:
                    l_min = edge_min
                if l < l_min:
                    l = l_min
                if edge_max:
                    l_max = edge_max
                if l > l_max:
                    l = l_max
                direction = _get_lambda(edge_v, target_v)
                l *= direction
                if u in free_vkeys:
                    new_u_xyz = add_vectors(formdiagram.vertex_coordinates(v), scale_vector(target_v, -1 * l))
                    new_form_xyz[u].append(new_u_xyz)
                if v in free_vkeys:
                    new_v_xyz = add_vectors(formdiagram.vertex_coordinates(u), scale_vector(target_v, l))
                    new_form_xyz[v].append(new_v_xyz)
            for vkey in free_vkeys:
                final_xyz = centroid_points(new_form_xyz[vkey])
                formdiagram.vertex_update_xyz(vkey, final_xyz)
        if weight != 1:
            volmesh_planarise(volmesh,
                              kmax=1,
                              target_normals=target_normals)
        for fkey in boundary_fkeys:
            f_normal = volmesh.halfface_normal(fkey)
            target_normal = target_normals[fkey]
            b_perpness = angle_vectors(f_normal, target_normal, deg=True)
            if b_perpness > deviation_boundary_perp:
                deviation_boundary_perp = b_perpness
        perpness = _check_deviation(volmesh, formdiagram)
        if perpness < tolerance and deviation_boundary_perp < tolerance_boundary:
            if print_result_info:
                print_result('Reciprocation', k, perpness)
            break
        if callback:
            callback(volmesh, formdiagram, k, callback_args)
def volmesh_reciprocate ( volmesh , formdiagram , kmax = 100 , weight = 1.0 , fix_vkeys = [ ] , edge_min = 0.1 , edge_max = 10000 , tolerance = 1.0 , tolerance_boundary = 1.0 , callback = None , callback_args = None , print_result_info = False ) : if callback : if not callable ( callback ) : raise Exception ( 'Callback is not callable.' ) free_vkeys = list ( set ( formdiagram . vertices ( ) ) - set ( fix_vkeys ) ) init_normals = { fkey : volmesh . halfface_normal ( fkey ) for fkey in volmesh . faces ( ) } boundary_fkeys = volmesh . halffaces_on_boundaries ( ) target_vectors = { } target_normals = { } for u , v in formdiagram . edges ( ) : u_hfkey = volmesh . cell_pair_halffaces ( u , v ) [ 0 ] face_normal = scale_vector ( volmesh . halfface_normal ( u_hfkey ) , weight ) edge_vector = scale_vector ( formdiagram . edge_vector ( u , v ) , 1 - weight ) target = normalize_vector ( add_vectors ( face_normal , edge_vector ) ) target_vectors [ ( u , v ) ] = { 'fkey' : u_hfkey , 'target' : target } target_normals [ u_hfkey ] = target for fkey in boundary_fkeys : target_normals [ fkey ] = init_normals [ fkey ] for k in range ( kmax ) : deviation_boundary_perp = 0 if weight != 0 : new_form_xyz = { vkey : [ ] for vkey in formdiagram . vertices ( ) } for u , v in target_vectors : target_v = target_vectors [ ( u , v ) ] [ 'target' ] edge_v = formdiagram . edge_vector ( u , v , unitized = False ) l = length_vector ( edge_v ) l_min = formdiagram . edge_attribute ( ( u , v ) , 'l_min' ) l_max = formdiagram . edge_attribute ( ( u , v ) , 'l_max' ) if edge_min : l_min = edge_min if l < l_min : l = l_min if edge_max : l_max = edge_max if l > l_max : l = l_max direction = _get_lambda ( edge_v , target_v ) l *= direction if u in free_vkeys : new_u_xyz = add_vectors ( formdiagram . vertex_coordinates ( v ) , scale_vector ( target_v , - 1 * l ) ) new_form_xyz [ u ] . append ( new_u_xyz ) if v in free_vkeys : new_v_xyz = add_vectors ( formdiagram . vertex_coordinates ( u ) , scale_vector ( target_v , l ) ) new_form_xyz [ v ] . append ( new_v_xyz ) for vkey in free_vkeys : final_xyz = centroid_points ( new_form_xyz [ vkey ] ) formdiagram . vertex_update_xyz ( vkey , final_xyz ) if weight != 1 : volmesh_planarise ( volmesh , kmax = 1 , target_normals = target_normals ) for fkey in boundary_fkeys : f_normal = volmesh . halfface_normal ( fkey ) target_normal = target_normals [ fkey ] b_perpness = angle_vectors ( f_normal , target_normal , deg = True ) if b_perpness > deviation_boundary_perp : deviation_boundary_perp = b_perpness perpness = _check_deviation ( volmesh , formdiagram ) if perpness < tolerance and deviation_boundary_perp < tolerance_boundary : if print_result_info : print_result ( 'Reciprocation' , k , perpness ) break if callback : callback ( volmesh , formdiagram , k , callback_args )
Perpendicularizes the faces of the polyhedral force diagram to the corresponding dual edges in the polyhedral form diagram.
Perpendicularizes the faces of the polyhedral force diagram to the corresponding dual edges in the polyhedral form diagram .
"""Perpendicularizes the faces of the polyhedral force diagram to the corresponding dual edges in the polyhedral form diagram.

    Parameters
    ----------
    volmesh : VolMesh
        A volmesh object representing a polyhedral force diagram.
    formdiagram : VolMesh or Network
        A network object representing a polyhedral form diagram.
    kmax : int, optional [100]
        Maximum number of iterations.
    weight : float, optional [1.0]
        A float, between 0 and 1, which determines how much each diagram changes. 1 changes the form diagram only, while 0 changes the force diagram only. Default is ``1.0``.
    fix_vkeys : list, optional []
        List of vkeys to fix.
    edge_min : float, optional [None]
        Value for minimum edge length to be imposed.
    edge_max : float, optional [None]
        Value for maximum edge length allowed.
    tolerance: float, optional [0.001]
        Value for convergence tolerance. Deviation is measured by the the dot product of the input and target vectors.
    callback : callable, optional [None]
        A user-defined callback function to be executed after every iteration.
    callback_args : tuple, optional [None]
        Additional parameters to be passed to the callback.
    print_result_info : bool, optional
        If True, print the result of the algorithm.

    Raises
    ------
    Exception
        If a callback is provided, but it is not callable.

    Notes
    -----
    The orientations of the boundary faces of the polyhedral force diagram are always fixed by default.

    .. seealso ::
        compas.geometry.network_parallelise_edges

    """ # -------------------------------------------------------------------------- #   1. compute target vectors # -------------------------------------------------------------------------- # -------------------------------------------------------------------------- #   2. loop # -------------------------------------------------------------------------- # ---------------------------------------------------------------------- #   3. update form diagram # ---------------------------------------------------------------------- # target edge length ------------------------------------------- # min edge # max edge # check edge orientation --------------------------------------- # collect new coordinates -------------------------------------- # compute new vertex coordinates ----------------------------------- # ---------------------------------------------------------------------- #   4. update force diagram # ---------------------------------------------------------------------- # boundary perpness # 1 - abs(dot_vectors(f_normal, target_normal)) # ---------------------------------------------------------------------- #   5. check convergence # ---------------------------------------------------------------------- # callback / conduit ---------------------------------------------------
volmesh
formdiagram
kmax
weight
fix_vkeys
edge_min
edge_max
tolerance
tolerance_boundary
callback
callback_args
print_result_info