Exemple #1
0
def on_helix_checker(b1, b2):
    """Return a function that checks if a bud lies on the helix going through the provided buds."""
    hdiff = b2.height - b1.height
    adiff = norm_angle(b2.angle - b1.angle)

    if b2.height < b1.height:
        hdiff = -hdiff
        adiff = -adiff

    # the buds are on the same height - a circle, not a helix
    if abs(hdiff) < 0.001:
        return lambda b: abs(b.height - b1.height) < 0.001

    # the buds have the same angle - a vertical line, not a helix
    if abs(adiff) < 0.001:
        return lambda b: abs(norm_angle(b.angle - b1.angle)) < 0.001

    slope = math.atan(adiff / hdiff)

    def on_helix(b):
        """Check if the provided bud is on the given helix.

        The helix is created as (bud.radius, t - bud.angle, slope*t - bud.height). This
        make things easier, as it's really just the unit helix moved up by the selected
        buds height, moved sideways by the selected buds angle and then streched so that
        it goes through the second bud. The slope is calculated from the tangent of how
        much the helix was moved laterly and vertically.
        """
        angle_diff = norm_angle(b.angle - b1.angle)
        height_diff = norm_angle(slope * (b.height - b1.height))
        return abs(angle_diff - height_diff) < min(1, abs(slope)) * b.scale

    return on_helix
Exemple #2
0
    def on_helix(b):
        """Check if the provided bud is on the given helix.

        The helix is created as (bud.radius, t - bud.angle, slope*t - bud.height). This
        make things easier, as it's really just the unit helix moved up by the selected
        buds height, moved sideways by the selected buds angle and then streched so that
        it goes through the second bud. The slope is calculated from the tangent of how
        much the helix was moved laterly and vertically.
        """
        angle_diff = norm_angle(b.angle - b1.angle)
        height_diff = norm_angle(slope * (b.height - b1.height))
        return abs(angle_diff - height_diff) < min(1, abs(slope)) * b.scale
Exemple #3
0
def occlusion_cone(b1, b2):
    """Return a function that tests whether a given bud lies in the occlusion cone behind b2 from b1."""
    dir_vec = dir_vector(b2, b1)
    apex = middle_point(b1, b2)

    # because of the overwrapping of angles, there is a degenerative case when the cone lies on the angle axis
    if dir_vec[1] == 0 and dir_vec[2] == 0:
        h = length((norm_angle(b2.angle - apex[0]), b2.height - apex[1],
                    b2.radius - apex[2]))
    else:
        h = length((norm_angle(b2.angle - apex[0]), b2.height - apex[1],
                    b2.radius - apex[2]))
    return in_cone_checker(apex, dir_vec, b2.scale, h)
Exemple #4
0
    def helix_pos(height):
        """Check if the provided bud is on the given helix.

        The helix is created as (bud.radius, t - bud.angle, slope*t - bud.height). This
        make things easier, as it's really just the unit helix moved up by the selected
        buds height, moved sideways by the selected buds angle and then streched so that
        it goes through the second bud. The slope is calculated from the tangent of how
        much the helix was moved laterly and vertically.
        """
        # calculate the i which is closest to the origin. This is derived from
        #
        #   h(t) = slope * t + b1.height
        #   t(i) = sign *  i * math.radians(5)
        #   h(i) = slope * (sign *  i * math.radians(5)) + b1.height
        #   h(i) = 0
        i0 = int(round(-b1.height / (slope * math.radians(5))))

        # next calculate the i which is closest to the height (h(i) == height)
        iend = int(round((height - b1.height) / (slope * math.radians(5))))

        for i in range(min(i0, iend), max(i0, iend)):
            t = i * math.radians(5)
            angle, height, radius = norm_angle(
                t + b1.angle), slope * t + b1.height, b1.radius + b1.scale
            yield b1.cyl_to_cart(angle, height, radius)
Exemple #5
0
def helix(b1, b2):
    """Return a generator that returns (cartesian) points on a helix going through the provided buds.

    Each point is b1.scale away from the next one.
    """
    hdiff = b2.height - b1.height
    adiff = norm_angle(b2.angle - b1.angle)

    # the buds are on the same height - a circle, not a helix
    if abs(hdiff) < 0.001:

        def circle(height):
            return (b1.cyl_to_cart(math.radians(a), b1.height,
                                   b1.radius + b1.scale)
                    for a in range(0, 360, 5))

        return circle

    # the buds have the same angle - a vertical line, not a helix
    if abs(adiff) < 0.001:

        def line(height):
            return (b1.cyl_to_cart(b1.angle, h, b1.radius + b1.scale)
                    for h in range(int(round(height))))

        return line

    slope = hdiff / adiff

    def helix_pos(height):
        """Check if the provided bud is on the given helix.

        The helix is created as (bud.radius, t - bud.angle, slope*t - bud.height). This
        make things easier, as it's really just the unit helix moved up by the selected
        buds height, moved sideways by the selected buds angle and then streched so that
        it goes through the second bud. The slope is calculated from the tangent of how
        much the helix was moved laterly and vertically.
        """
        # calculate the i which is closest to the origin. This is derived from
        #
        #   h(t) = slope * t + b1.height
        #   t(i) = sign *  i * math.radians(5)
        #   h(i) = slope * (sign *  i * math.radians(5)) + b1.height
        #   h(i) = 0
        i0 = int(round(-b1.height / (slope * math.radians(5))))

        # next calculate the i which is closest to the height (h(i) == height)
        iend = int(round((height - b1.height) / (slope * math.radians(5))))

        for i in range(min(i0, iend), max(i0, iend)):
            t = i * math.radians(5)
            angle, height, radius = norm_angle(
                t + b1.angle), slope * t + b1.height, b1.radius + b1.scale
            yield b1.cyl_to_cart(angle, height, radius)

    return helix_pos
Exemple #6
0
def middle_point(b1, b2):
    """Find the approximate cutting point of the inner tangents of the provided buds."""
    dir_vec = dir_vector(b2, b1)
    line_len = length(dir_vec)

    normed_dir = vect_mul(dir_vec, 1 / line_len)

    d1 = (b1.scale * line_len) / (b1.scale + b2.scale)

    return (
        norm_angle(b1.angle + d1 * normed_dir[0]),
        b1.height + d1 * normed_dir[1],
        b1.radius + d1 * normed_dir[2],
    )
Exemple #7
0
def dir_vector(b1: Bud, b2: Bud) -> tuple:
    """Return a direction vector for the provided buds."""
    return (norm_angle(b1.angle - b2.angle), b1.height - b2.height,
            b1.radius - b2.radius)
Exemple #8
0
 def checker(bud):
     """Return whether this bud is occluded by b2."""
     pl = a * norm_angle(bud.angle - b2.angle) + b * (
         bud.height - b2.height) + c * (bud.radius - b2.radius)
     return pl >= 0
Exemple #9
0
    def checker(bud):
        # the diff between b1 and bud
        diff = (norm_angle(bud.angle - b1.angle), bud.height - b1.height,
                bud.radius - b1.radius)

        return length(cross_product(diff, dir_vec)) / length(dir_vec)
Exemple #10
0
def test_norm_angle(angle, normed):
    """Check whether normalizing angles works."""
    assert approx_equal(norm_angle(angle), normed)