Exemple #1
0
def angle_vectors(u, v, deg=False, tol=0.0):
    """Compute the smallest angle between two vectors.

    Parameters
    ----------
    u : [float, float, float] | :class:`compas.geometry.Vector`
        XYZ components of the first vector.
    v : [float, float, float] | :class:`compas.geometry.Vector`
        XYZ components of the second vector.
    deg : bool, optional
        If True, returns the angle in degrees.
    tol : float, optional
        Tolerance for the length of the vectors.

    Returns
    -------
    float
        The smallest angle in radians (in degrees if ``deg == True``).
        The angle is always positive.

    Examples
    --------
    >>> angle_vectors([0.0, 1.0, 0.0], [1.0, 0.0, 0.0])
    1.57079

    """
    L = length_vector(u) * length_vector(v)
    if tol and L < tol:
        return 0
    a = dot_vectors(u, v) / L
    a = max(min(a, 1), -1)

    if deg:
        return degrees(acos(a))
    return acos(a)
Exemple #2
0
def angle_vectors(u, v, deg=False, tol=0.0):
    """Compute the smallest angle between two vectors.

    Parameters
    ----------
    u : sequence of float
        XYZ components of the first vector.
    v : sequence of float
        XYZ components of the second vector.
    deg : boolean
        returns angle in degrees if True

    Returns
    -------
    float
        The smallest angle in radians (in degrees if deg == True).
        The angle is always positive.

    Examples
    --------
    >>> angle_vectors([0.0, 1.0, 0.0], [1.0, 0.0, 0.0])

    """
    L = length_vector(u) * length_vector(v)
    if tol and L < tol:
        return 0
    a = dot_vectors(u, v) / L
    a = max(min(a, 1), -1)

    if deg:
        return degrees(acos(a))
    return acos(a)
Exemple #3
0
def distance_point_point(a, b):
    """Compute the distance bewteen a and b.

    Parameters
    ----------
    a : sequence of float
        XYZ coordinates of point a.
    b : sequence of float
        XYZ coordinates of point b.

    Returns
    -------
    float
        Distance bewteen a and b.

    Examples
    --------
    >>> distance_point_point([0.0, 0.0, 0.0], [2.0, 0.0, 0.0])
    2.0

    See Also
    --------
    distance_point_point_xy

    """
    ab = subtract_vectors(b, a)
    return length_vector(ab)
Exemple #4
0
def distance_point_point(a, b):
    """Compute the distance bewteen a and b.

    Parameters
    ----------
    a : [float, float, float] | :class:`compas.geometry.Point`
        XYZ coordinates of point a.
    b : [float, float, float] | :class:`compas.geometry.Point`
        XYZ coordinates of point b.

    Returns
    -------
    float
        Distance bewteen a and b.

    Examples
    --------
    >>> distance_point_point([0.0, 0.0, 0.0], [2.0, 0.0, 0.0])
    2.0

    See Also
    --------
    distance_point_point_xy

    """
    ab = subtract_vectors(b, a)
    return length_vector(ab)
Exemple #5
0
def normal_triangle_xy(triangle, unitized=True):
    """Compute the normal vector of a triangle assumed to lie in the XY plane.

    Parameters
    ----------
    triangle : [point, point, point] | :class:`compas.geometry.Polygon`
        A list of triangle point coordinates.
        Z-coordinates are ignored.
    unitized : bool, optional
        If True, unitize the normal vector.

    Returns
    -------
    [float, float, float]
        The normal vector, which is a vector perpendicular to the XY plane.

    Raises
    ------
    AssertionError
        If the triangle does not have three vertices.

    """
    a, b, c = triangle
    ab = subtract_vectors_xy(b, a)
    ac = subtract_vectors_xy(c, a)
    n = cross_vectors_xy(ab, ac)
    if not unitized:
        return n
    return [0, 0, n[2] / length_vector(n)]
Exemple #6
0
def normal_triangle(triangle, unitized=True):
    """Compute the normal vector of a triangle.

    Parameters
    ----------
    triangle : list of list
        A list of triangle point coordinates.

    Returns
    -------
    list
        The normal vector.

    Raises
    ------
    ValueError
        If the triangle does not have three vertices.

    """
    assert len(triangle) == 3, "Three points are required."
    a, b, c = triangle
    ab = subtract_vectors(b, a)
    ac = subtract_vectors(c, a)
    n = cross_vectors(ab, ac)
    if not unitized:
        return n
    lvec = 1 / length_vector(n)
    return [lvec * n[0], lvec * n[1], lvec * n[2]]
Exemple #7
0
def normal_triangle_xy(triangle, unitized=True):
    """Compute the normal vector of a triangle assumed to lie in the XY plane.

    Parameters
    ----------
    triangle : list of list
        A list of triangle point coordinates.
        Z-coordinates are ignored.

    Returns
    -------
    list
        The normal vector, which is a vector perpendicular to the XY plane.

    Raises
    ------
    ValueError
        If the triangle does not have three vertices.

    """
    a, b, c = triangle
    ab = subtract_vectors_xy(b, a)
    ac = subtract_vectors_xy(c, a)
    n = cross_vectors_xy(ab, ac)
    if not unitized:
        return n
    return [0, 0, n[2] / length_vector(n)]
Exemple #8
0
def centroid_polygon_edges(polygon):
    """Compute the centroid of the edges of a polygon.

    Parameters
    ----------
    polygon : list of point
        A sequence of polygon point coordinates.

    Returns
    -------
    list
        The XYZ coordinates of the centroid.

    Notes
    -----
    The centroid of the edges is the centroid of the midpoints of the edges, with
    each midpoint weighted by the length of the corresponding edge proportional
    to the total length of the boundary.
    """
    L = 0
    cx = 0
    cy = 0
    cz = 0
    p = len(polygon)
    for i in range(-1, p - 1):
        p1 = polygon[i]
        p2 = polygon[i + 1]
        d = length_vector(subtract_vectors(p2, p1))
        cx += 0.5 * d * (p1[0] + p2[0])
        cy += 0.5 * d * (p1[1] + p2[1])
        cz += 0.5 * d * (p1[2] + p2[2])
        L += d
    return [cx / L, cy / L, cz / L]
Exemple #9
0
def normal_triangle(triangle, unitized=True):
    """Compute the normal vector of a triangle.

    Parameters
    ----------
    triangle : [point, point, point] | :class:`compas.geometry.Polygon`
        A list of triangle point coordinates.
    unitized : bool, optional
        If True, unitize the normal vector.

    Returns
    -------
    [float, float, float]
        The normal vector.

    Raises
    ------
    AssertionError
        If the triangle does not have three vertices.

    """
    assert len(triangle) == 3, "Three points are required."
    a, b, c = triangle
    ab = subtract_vectors(b, a)
    ac = subtract_vectors(c, a)
    n = cross_vectors(ab, ac)
    if not unitized:
        return n
    lvec = 1 / length_vector(n)
    return [lvec * n[0], lvec * n[1], lvec * n[2]]
Exemple #10
0
def normal_polygon(polygon, unitized=True):
    """Compute the normal of a polygon defined by a sequence of points.

    Parameters
    ----------
    polygon : list of list
        A list of polygon point coordinates.

    Returns
    -------
    list
        The normal vector.

    Raises
    ------
    ValueError
        If less than three points are provided.

    Notes
    -----
    The points in the list should be unique. For example, the first and last
    point in the list should not be the same.

    """
    p = len(polygon)

    assert p > 2, "At least three points required"

    nx = 0
    ny = 0
    nz = 0

    o = centroid_points(polygon)
    a = polygon[-1]
    oa = subtract_vectors(a, o)

    for i in range(p):
        b = polygon[i]
        ob = subtract_vectors(b, o)
        n = cross_vectors(oa, ob)
        oa = ob

        nx += n[0]
        ny += n[1]
        nz += n[2]

    if not unitized:
        return nx, ny, nz

    length = length_vector([nx, ny, nz])

    return nx / length, ny / length, nz / length
Exemple #11
0
def circle_from_points(a, b, c):
    """Construct a circle from three points.

    Parameters
    ----------
    a : [float, float, float] | :class:`compas.geometry.Point`
        XYZ coordinates.
    b : [float, float, float] | :class:`compas.geometry.Point`
        XYZ coordinates.
    c : [float, float, float] | :class:`compas.geometry.Point`
        XYZ coordinates.

    Returns
    -------
    [float, float, float]
        Center of the circle.
    float
        Radius of the circle.
    [float, float, float]
        Normal of the plane containing the circle.

    Notes
    -----
    For more information, see [1]_.

    References
    ----------
    .. [1] Wikipedia. *Circumscribed circle*.
           Available at: https://en.wikipedia.org/wiki/Circumscribed_circle.

    Examples
    --------
    >>>

    """
    ab = subtract_vectors(b, a)
    cb = subtract_vectors(b, c)
    ba = subtract_vectors(a, b)
    ca = subtract_vectors(a, c)
    ac = subtract_vectors(c, a)
    bc = subtract_vectors(c, b)
    normal = normalize_vector(cross_vectors(ab, ac))
    d = 2 * length_vector_sqrd(cross_vectors(ba, cb))
    A = length_vector_sqrd(cb) * dot_vectors(ba, ca) / d
    B = length_vector_sqrd(ca) * dot_vectors(ab, cb) / d
    C = length_vector_sqrd(ba) * dot_vectors(ac, bc) / d
    Aa = scale_vector(a, A)
    Bb = scale_vector(b, B)
    Cc = scale_vector(c, C)
    center = sum_vectors([Aa, Bb, Cc])
    radius = length_vector(subtract_vectors(a, center))
    return center, radius, normal
Exemple #12
0
def distance_point_line(point, line):
    """Compute the distance between a point and a line.

    Parameters
    ----------
    point : list, tuple
        Point location.
    line : list, tuple
        Line defined by two points.

    Returns
    -------
    float
        The distance between the point and the line.

    Notes
    -----
    This implementation computes the *right angle distance* from a point P to a
    line defined by points A and B as twice the area of the triangle ABP divided
    by the length of AB [1]_.

    References
    ----------
    .. [1] Wikipedia. *Distance from a point to a line*.
           Available at: https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line

    Examples
    --------
    >>>

    """
    a, b = line
    ab = subtract_vectors(b, a)
    pa = subtract_vectors(a, point)
    pb = subtract_vectors(b, point)
    length = length_vector(cross_vectors(pa, pb))
    length_ab = length_vector(ab)
    return length / length_ab
Exemple #13
0
def area_polygon(polygon):
    """Compute the area of a polygon.

    Parameters
    ----------
    polygon : sequence[point] | :class:`compas.geometry.Polygon`
        The XYZ coordinates of the vertices/corners of the polygon.
        The vertices are assumed to be in order.
        The polygon is assumed to be closed:
        the first and last vertex in the sequence should not be the same.

    Returns
    -------
    float
        The area of the polygon.

    Examples
    --------
    >>>
    """
    o = centroid_points(polygon)
    a = polygon[-1]
    b = polygon[0]
    oa = subtract_vectors(a, o)
    ob = subtract_vectors(b, o)
    n0 = cross_vectors(oa, ob)
    area = 0.5 * length_vector(n0)
    for i in range(0, len(polygon) - 1):
        oa = ob
        b = polygon[i + 1]
        ob = subtract_vectors(b, o)
        n = cross_vectors(oa, ob)
        if dot_vectors(n, n0) > 0:
            area += 0.5 * length_vector(n)
        else:
            area -= 0.5 * length_vector(n)
    return area
Exemple #14
0
def area_triangle(triangle):
    """Compute the area of a triangle defined by three points.

    Parameters
    ----------
    triangle : list of list
        XYZ coordinates of the corners of the triangle.

    Returns
    -------
    float
        The area of the triangle.

    """
    return 0.5 * length_vector(normal_triangle(triangle, False))
Exemple #15
0
def area_triangle(triangle):
    """Compute the area of a triangle defined by three points.

    Parameters
    ----------
    triangle : [point, point, point] | :class:`compas.geometry.Polygon`
        XYZ coordinates of the corners of the triangle.

    Returns
    -------
    float
        The area of the triangle.

    """
    return 0.5 * length_vector(normal_triangle(triangle, False))
Exemple #16
0
def distance_line_line(l1, l2, tol=0.0):
    r"""Compute the shortest distance between two lines.

    Parameters
    ----------
    l1 : tuple
        Two points defining a line.
    l2 : tuple
        Two points defining a line.

    Returns
    -------
    float
        The distance between the two lines.

    Notes
    -----
    The distance is the absolute value of the dot product of a unit vector that
    is perpendicular to the two lines, and the vector between two points on the lines ([1]_, [2]_).

    If each of the lines is defined by two points (:math:`l_1 = (\mathbf{x_1}, \mathbf{x_2})`,
    :math:`l_2 = (\mathbf{x_3}, \mathbf{x_4})`), then the unit vector that is
    perpendicular to both lines is...

    References
    ----------
    .. [1] Weisstein, E.W. *Line-line Distance*.
           Available at: http://mathworld.wolfram.com/Line-LineDistance.html.
    .. [2] Wikipedia. *Skew lines Distance*.
           Available at: https://en.wikipedia.org/wiki/Skew_lines#Distance.

    Examples
    --------
    >>>

    """
    a, b = l1
    c, d = l2
    ab = subtract_vectors(b, a)
    cd = subtract_vectors(d, c)
    ac = subtract_vectors(c, a)
    n = cross_vectors(ab, cd)
    length = length_vector(n)
    if length <= tol:
        return distance_point_point(closest_point_on_line(l1[0], l2), l1[0])
    n = scale_vector(n, 1.0 / length)
    return fabs(dot_vectors(n, ac))
Exemple #17
0
def angle_vectors_signed(u, v, normal, deg=False, threshold=1e-3):
    """Computes the signed angle between two vectors.

    It calculates the angle such that rotating vector u about the normal by
    angle would result in a vector that looks into the same direction as v.

    Parameters
    ----------
    u : [float, float, float] | :class:`compas.geometry.Vector`
        XYZ components of the first vector.
    v : [float, float, float] | :class:`compas.geometry.Vector`
        XYZ components of the second vector.
    normal : [float, float, float] | :class:`compas.geometry.Vector`
        XYZ components of the plane's normal spanned by u and v.
    deg : bool, optional
        If True, returns the angle in degrees.
    threshold : float, optional
        The threshold (radians) used to consider if the angle is zero.

    Returns
    -------
    float
        The signed angle in radians (in degrees if deg == True).

    Examples
    --------
    >>> normal = [0.0, 0.0, 1.0]
    >>> angle_vectors_signed([0.0, 1.0, 0.0], [1.0, 0.0, 0.0], normal)
    -1.57079
    """
    angle = angle_vectors(u, v)
    normal_uv = cross_vectors(u, v)

    if length_vector(normal_uv) > threshold:
        # check if normal_uv has the same direction as normal
        angle_btw_normals = angle_vectors(normal, normal_uv)
        if angle_btw_normals > threshold:
            angle *= -1

    if deg:
        return degrees(angle)
    else:
        return angle
Exemple #18
0
            n = cross_vectors(ab, ac)
            V += dot_vectors(a, n)
    return V / 6.


# ==============================================================================
# Main
# ==============================================================================

if __name__ == "__main__":

    from compas.geometry._core import Polyhedron

    cube = Polyhedron.generate(6)

    L = length_vector(subtract_vectors(cube.vertices[0], cube.vertices[1]))

    V1 = L * L * L
    V2 = volume_polyhedron(cube)

    print(V1 - V2 <= 1e-6)

    # plotter = Plotter(figsize=(10, 7))

    # polygon = [
    #     [0, 0, 0],
    #     [1.0, 0, 0],
    #     [1.0, 1.0, 0],
    #     [0.5, 0.0, 0],
    #     [0, 1.0, 0]
    # ]
Exemple #19
0
def centroid_polygon(polygon):
    r"""Compute the centroid of the surface of a polygon.

    Parameters
    ----------
    polygon : list of point
        A sequence of polygon point coordinates.

    Returns
    -------
    list
        The XYZ coordinates of the centroid.

    Notes
    -----
    The centroid is the centre of gravity of the polygon surface if mass would be
    uniformly distributed over it.

    It is calculated by triangulating the polygon surface with respect to the centroid
    of the polygon vertices, and then computing the centroid of the centroids of
    the individual triangles, weighted by the corresponding triangle area in
    proportion to the total surface area.

    .. math::

        c_x = \frac{1}{A} \sum_{i=1}^{N} A_i \cdot c_{x,i}
        c_y = \frac{1}{A} \sum_{i=1}^{N} A_i \cdot c_{y,i}
        c_z = \frac{1}{A} \sum_{i=1}^{N} A_i \cdot c_{z,i}

    Warning
    -------
    The polygon need not be convex.

    The polygon need not be flat. However, it is unclear what the meaning of the
    centroid is in that case.

    The polygon may be self-intersecting. However, it is unclear what the meaning
    of the centroid is in that case.

    Examples
    --------
    >>> polygon = [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 0.0]]
    >>> centroid_polygon(polygon)
    """
    p = len(polygon)

    assert p > 2, "At least three points required"

    if p == 3:
        return centroid_points(polygon)

    cx, cy, cz = 0.0, 0.0, 0.0
    A2 = 0

    o = centroid_points(polygon)
    a = polygon[-1]
    b = polygon[0]
    oa = subtract_vectors(a, o)
    ob = subtract_vectors(b, o)
    n0 = cross_vectors(oa, ob)

    x, y, z = centroid_points([o, a, b])
    a2 = length_vector(n0)

    A2 += a2
    cx += a2 * x
    cy += a2 * y
    cz += a2 * z

    for i in range(1, len(polygon)):
        a = b
        b = polygon[i]

        oa = ob
        ob = subtract_vectors(b, o)

        n = cross_vectors(oa, ob)
        x, y, z = centroid_points([o, a, b])

        if dot_vectors(n, n0) > 0:
            a2 = length_vector(n)
        else:
            a2 = -length_vector(n)

        A2 += a2
        cx += a2 * x
        cy += a2 * y
        cz += a2 * z

    if A2 == 0:
        return polygon[0]

    return [cx / A2, cy / A2, cz / A2]