def scale_verts_by_bone(self, pbone, armature, mesh_object, vert_co, weight=1.0):
        # verts = mesh_object.data.vertices
        bone = armature.data.bones[pbone.name]
        bone_head = self.init_bone_positions[bone.name]["head"]
        bone_tail = self.init_bone_positions[bone.name]["tail"]
        bone_axis_x = (bone_tail - bone_head).normalized().xz
        bone_axis_y = bone_axis_x.orthogonal().normalized()

        world_axis_x = Vector((bone_axis_x.dot(Vector((1, 0))), bone_axis_y.dot(Vector((1, 0)))))
        world_axis_y = Vector((bone_axis_x.dot(Vector((0, 1))), bone_axis_y.dot(Vector((0, 1)))))

        bone_system_origin = (mesh_object.matrix_world.inverted() * (armature.matrix_world * bone_head)).xz

        bone_scale = pbone.matrix.to_scale()
        bone_scale_2d = Vector(( self.lerp( 1.0, bone_scale.y, weight), self.lerp(1.0, bone_scale.x, weight) ))

        vert_delta_co = vert_co.xz
        vert_delta_co -= bone_system_origin
        vert_delta_co = Vector((bone_axis_x.dot(vert_delta_co), bone_axis_y.dot(vert_delta_co)))
        vert_delta_co = Vector((vert_delta_co.x * bone_scale_2d.x, vert_delta_co.y * bone_scale_2d.y))
        vert_delta_co = Vector((world_axis_x.dot(vert_delta_co), world_axis_y.dot(vert_delta_co)))
        vert_delta_co += bone_system_origin

        scaled_vert_co = Vector((vert_delta_co.x, 0, vert_delta_co.y))
        return scaled_vert_co
Esempio n. 2
0
    def _clip_face(self, verts, clip_planes, clip_side='negative'):
        for clip in clip_planes:
            clip_normal = Vector(clip.normal)
            clip_distance = clip.distance

            if clip_side == 'positive':
                clip_normal = -clip_normal
                clip_distance = -clip_distance

            new_verts = []
            for cur_index, cur_vert in enumerate(verts):
                prev_index = (cur_index + len(verts) - 1) % len(verts)
                prev_vert = verts[prev_index]

                dist_cur = clip_normal.dot(cur_vert) + clip_distance
                dist_prev = clip_normal.dot(prev_vert) + clip_distance

                if dist_cur >= 0 and dist_prev >= 0:
                    new_verts.append(cur_vert)
                elif dist_cur >= 0 and dist_prev < 0:
                    lerp = -dist_prev / (dist_cur - dist_prev)
                    new_verts.append(prev_vert + lerp * (cur_vert - prev_vert))
                    new_verts.append(cur_vert)
                elif dist_cur < 0 and dist_prev >= 0:
                    lerp = -dist_prev / (dist_cur - dist_prev)
                    new_verts.append(prev_vert + lerp * (cur_vert - prev_vert))

            verts = new_verts

        return verts
Esempio n. 3
0
    def autoscale(self):
        symmetry = nmv.physics.hex_symmetry_space if self.hex_mode else nmv.physics.symmetry_space

        for vert in self.bm.verts:
            u = Vector(self.field[vert.index])
            v = u.cross(vert.normal)
            ang = 0
            last_vec = u
            for loop in vert.link_loops:
                vert1 = loop.link_loop_next.vert
                vert2 = loop.link_loop_next.link_loop_next.vert
                if not last_vec:
                    vert1_vec = Vector(self.field[vert1.index])
                else:
                    vert1_vec = last_vec

                vert2_vec = nmv.physics.best_matching_vector(
                    symmetry(self.field[vert2.index], vert2.normal), vert1_vec)

                vert1_vec = Vector((vert1_vec.dot(u), vert1_vec.dot(v)))
                vert2_vec = Vector((vert2_vec.dot(u), vert2_vec.dot(v)))

                ang += vert1_vec.angle_signed(vert2_vec)
            self.scale[vert.index] = ang
        for i in range(20):
            self.scale += self.scale[self.walk_edges(0)]
            self.scale /= 2
        self.scale -= self.scale.min()
        self.scale /= self.scale.max()
Esempio n. 4
0
def uvproj_boxmap(co, normal, uv_scale, uv_offset):
    an = Vector([fabs(x) for x in normal])
    box_mask = hlsl_step(an.yzx, an)
    box_mask = Vector(hlsl_product(box_mask, hlsl_step(an.zxy, an)))
    sn = hlsl_sign(normal)
    box_mask = hlsl_product(box_mask, Vector((sn.x, -sn.y, 1.0)))
    uv_x = box_mask.dot(co.yxx) * uv_scale.x
    box_mask = hlsl_product(box_mask, Vector((sn.x, -sn.y, sn.z)))
    uv_y = box_mask.dot(co.zzy) * uv_scale.y
    return uv_x, uv_y
Esempio n. 5
0
def add_object(self, context):
    verts = []
    edges = []
    faces = []

    errorOffset = Vector((0.02, 0.02, 0.02))

    AI_X = self.AIRadius * cos(self.AIAngle)
    AI_Y = self.AIRadius * sin(self.AIAngle)

    AIV = Vector((AI_X, AI_Y, 0))

    Player_X = self.PlayerDistance * cos(self.PlayerAngle)
    Player_Y = self.PlayerDistance * sin(self.PlayerAngle)

    PV = Vector((Player_X, Player_Y, 0))

    try:
        AngularDistance = acos(
            (AIV.dot(PV) / (self.AIRadius * self.PlayerDistance))) * 180 / pi
    except ValueError:
        AIV -= errorOffset
        AngularDistance = acos(
            (AIV.dot(PV) / (self.AIRadius * self.PlayerDistance))) * 180 / pi

    print("Angular Distance: ", AngularDistance - self.AIFOV)
    Total_X = AI_X + Player_X
    Total_Y = AI_Y + Player_Y

    AIFOV_LX = self.AIRadius * cos(self.AIAngle + self.AIFOV)
    AIFOV_LY = self.AIRadius * sin(self.AIAngle + self.AIFOV)
    AIFOV_RX = self.AIRadius * cos(self.AIAngle - self.AIFOV)
    AIFOV_RY = self.AIRadius * sin(self.AIAngle - self.AIFOV)

    verts.append(Vector((0, 0, 0)))

    verts.append(Vector((AIFOV_LX, AIFOV_LY, 0)))
    verts.append(Vector((AIFOV_RX, AIFOV_RY, 0)))
    edges.append([0, 1])
    edges.append([1, 2])
    edges.append([2, 0])

    verts.append(Vector((Player_X, Player_Y, 0)))
    edges.append([0, len(verts) - 1])

    if (AngularDistance - self.AIFOV * 180 / pi <= 0
            and self.AIRadius >= self.PlayerDistance):
        faces.append([2, 1, 0])

    mesh = bpy.data.meshes.new(name="New Object Mesh")
    mesh.from_pydata(verts, edges, faces)
    object_data_add(context, mesh, operator=self)
Esempio n. 6
0
    def detect_singularities(self):
        symmetry = nmv.physics.hex_symmetry_space if self.hex_mode else nmv.physics.symmetry_space
        cache = {}

        def symmetry_cached(vert):
            if vert in cache:
                return cache[vert]
            else:
                s = symmetry(self.field[vert.index], vert.normal)
                cache[vert] = s
                return s

        singularities = []

        if not self.hex_mode:
            for face in self.bm.faces:
                v0 = face.verts[0]
                v1 = face.verts[1]
                v2 = face.verts[2]
                vec0 = self.field[v0.index]
                vec1 = nmv.physics.best_matching_vector(
                    symmetry_cached(v1), vec0)
                v2_symmetry = symmetry_cached(v2)
                match0 = nmv.physics.best_matching_vector(v2_symmetry, vec0)
                match1 = nmv.physics.best_matching_vector(v2_symmetry, vec1)
                if match0.dot(match1) < 0.5:
                    singularities.append(face.calc_center_median())
        else:
            for vert in self.bm.verts:
                ang = 0
                u = nmv.physics.random_tangent_vector(vert.normal)
                v = u.cross(vert.normal)
                last_vec = None
                for loop in vert.link_loops:
                    vert1 = loop.link_loop_next.vert
                    vert2 = loop.link_loop_next.link_loop_next.vert
                    if not last_vec:
                        vert1_vec = symmetry_cached(vert1)[0]
                    else:
                        vert1_vec = last_vec
                    vert2_vec = nmv.physics.best_matching_vector(
                        symmetry_cached(vert2), vert1_vec)
                    last_vec = vert2_vec
                    vert1_vec = Vector((vert1_vec.dot(u), vert1_vec.dot(v)))
                    vert2_vec = Vector((vert2_vec.dot(u), vert2_vec.dot(v)))
                    ang += vert1_vec.angle_signed(vert2_vec)
                if ang > 0.9:
                    singularities.append(vert.co)

        self.singularities = singularities
Esempio n. 7
0
def lookatlh(eye, target, up):
    mz = Vector((eye[0] - target[0], eye[1] - target[1],
                 eye[2] - target[2])).normalized()  # inverse line of sight
    mx = Vector(up.cross(mz)).normalized()
    my = Vector(mz.cross(mx)).normalized()
    tx = mx.dot(eye)
    ty = my.dot(eye)
    tz = mz.dot(eye) * -1
    mat = Matrix()
    mat[0] = mx[0], mz[0], my[0], 0
    mat[1] = mx[2], mz[2], my[2], 0
    mat[2] = mx[1], mz[1], my[1], 0
    mat[3] = tx, tz, ty, 1

    return mat
    def execute(self, context):
        # extract user direction
        val = int(self.mode[-3:])
        if self.mode[0] == "N":
            val *= -1

        # No idea what changed
        if bpy.app.version > (2, 92, 0):
            val *= -1

        # get viewport rel to world axis and rotate
        rm = context.space_data.region_3d.view_matrix
        v = Vector(rm[2]).to_3d()

        x = v.dot(Vector((1, 0, 0)))
        y = v.dot(Vector((0, 1, 0)))
        z = v.dot(Vector((0, 0, 1)))

        xa, ya, za = abs(x), abs(y), abs(z)

        if xa > ya and xa > za:
            if val < 0 and x < 0 or val > 0 > x:
                val *= -1
            axis = True, False, False
            oa = "X"
        elif ya > xa and ya > za:
            if val < 0 and y < 0 or val > 0 > y:
                val *= -1
            axis = False, True, False
            oa = "Y"
        else:
            if val < 0 and z < 0 or val > 0 > z:
                val *= -1
            axis = False, False, True
            oa = "Z"

        bpy.ops.transform.rotate(value=radians(val),
                                 orient_axis=oa,
                                 orient_type='GLOBAL',
                                 orient_matrix_type='GLOBAL',
                                 constraint_axis=axis,
                                 mirror=True,
                                 use_proportional_edit=False,
                                 proportional_edit_falloff='SMOOTH',
                                 proportional_size=1,
                                 use_proportional_connected=False,
                                 use_proportional_projected=False)
        return {"FINISHED"}
Esempio n. 9
0
def cast_rays(obj_eval: Object,
              point: Vector,
              direction: Vector,
              mini_dist: float,
              round_type: str = "CEILING",
              edge_len: int = 0):
    """
    obj_eval   -- source object to test intersections for
    point      -- starting point for ray casting
    direction  -- cast ray in this direction
    mini_dist  -- Vector with miniscule amount to add after intersection
    round_type -- round final intersection location Vector with this type
    edge_len   -- distance to test for intersections
    """
    # initialize variables
    first_direction = False
    first_intersection = None
    next_intersection_loc = None
    last_intersection = None
    edge_intersects = False
    edge_len2 = round(edge_len + 0.000001, 6)
    starting_point = point
    intersections = 0
    # cast rays until no more rays to cast
    while True:
        _, location, normal, index = obj_eval.ray_cast(
            starting_point, direction)  #distance=edge_len*1.00000000001)
        if index == -1: break
        if intersections == 0:
            first_direction = direction.dot(normal)
        if edge_len != 0:
            dist = (location - point).length
            # get first and last intersection (used when getting materials of nearest (first or last intersected) face)
            if dist <= edge_len2:
                if intersections == 0:
                    edge_intersects = True
                    first_intersection = {
                        "idx": index,
                        "dist": dist,
                        "loc": location,
                        "normal": normal
                    }
                last_intersection = {
                    "idx": index,
                    "dist": edge_len - dist,
                    "loc": location,
                    "normal": normal
                }

            # set next_intersection_loc
            if next_intersection_loc is None:
                next_intersection_loc = location.copy()
        intersections += 1
        location = vec_round(location, precision=6, round_type=round_type)
        starting_point = location + mini_dist

    if edge_len != 0:
        return intersections, first_direction, first_intersection, next_intersection_loc, last_intersection, edge_intersects
    else:
        return intersections, first_direction
Esempio n. 10
0
def project(v, normal):
	p1, p2 = projectionAxis[tuple(normal)]
	p1, p2 = Vector(p1), Vector(p2)
	result = (p1.dot(v), p2.dot(v))
	print("res", result, "normal", tuple(normal))

	return result
Esempio n. 11
0
    def execute(self, context):

        bm = bmesh.new()
        bm.from_mesh(bpy.context.active_object.data)

        strength = bpy.context.scene.relax_strength
        tot = 50

        wm = bpy.context.window_manager
        for i in range(strength):
            wm.progress_begin(0, tot)
            for i in range(tot):
                wm.progress_update(i)

            for vert in bm.verts:
                avg = Vector()
                for edge in vert.link_edges:
                    other = edge.other_vert(vert)
                    avg += other.co
                avg /= len(vert.link_edges)
                avg -= vert.co
                avg -= avg.dot(vert.normal) * vert.normal
                vert.co += avg

        bm.normal_update()
        wm.progress_end()

        bm.to_mesh(bpy.context.active_object.data)

        bpy.context.active_object.data.update()

        bpy.context.view_layer.update()

        return {'FINISHED'}
Esempio n. 12
0
    def by_edge_dir(self, vertices, edges, faces):
        percent = self.inputs['Percent'].sv_get(default=[1.0])[0][0]
        direction = self.inputs['Direction'].sv_get()[0][0]
        dirvector = Vector(direction)
        dirlength = dirvector.length
        if dirlength <= 0:
            raise ValueError("Direction vector must have nonzero length!")

        values = []
        for i, j in edges:
            u = vertices[i]
            v = vertices[j]
            edge = Vector(u) - Vector(v)
            if edge.length > 0:
                value = abs(edge.dot(dirvector)) / (edge.length * dirlength)
            else:
                value = 0
            values.append(value)
        threshold = self.map_percent(values, percent)

        out_edges_mask = [(value >= threshold) for value in values]
        out_edges = [
            edge for (edge, mask) in zip(edges, out_edges_mask) if mask
        ]
        out_verts_mask = self.select_verts_by_faces(out_edges, vertices)
        out_faces_mask = self.select_faces_by_verts(out_verts_mask, faces)

        return out_verts_mask, out_edges_mask, out_faces_mask
Esempio n. 13
0
        def find_autocorrect_axis_angle(
                dir_vec_p_norm_b: Vector,
                bake_matrix: Matrix) -> Tuple[Vector, float]:
            """
            Given a vector of where the light will be pointed in X-Plane and our real rotation,
            find the Axis-Angle for how we need to rotate via animation to make the difference
            """
            def clamp(num: float, minimum: float, maximum: float) -> float:
                if num < minimum:
                    return minimum
                elif num > maximum:
                    return maximum
                else:
                    return num

            # Multiple bake matrix by Vector to get the direction of the Blender object

            dir_vec_b_norm = self.get_light_direction_b()

            # P is start rotation, and B is stop. As such, we have our axis of rotation.
            # "We take the X-Plane light and turn it until it matches what the artist wanted"
            axis_angle_vec_b = dir_vec_p_norm_b.cross(dir_vec_b_norm)

            dot_product_p_b = dir_vec_p_norm_b.dot(dir_vec_b_norm)
            if dot_product_p_b < 0:
                axis_angle_theta = math.pi - math.asin(
                    clamp(axis_angle_vec_b.magnitude, -1.0, 1.0))
            else:
                axis_angle_theta = math.asin(
                    clamp(axis_angle_vec_b.magnitude, -1.0, 1.0))
            return axis_angle_vec_b, axis_angle_theta
Esempio n. 14
0
def region_2d_to_vector_3d(region, rv3d, coord):
    """
    Return a direction vector from the viewport at the specific 2d region
    coordinate.

    :arg region: region of the 3D viewport, typically bpy.context.region.
    :type region: :class:`bpy.types.Region`
    :arg rv3d: 3D region data, typically bpy.context.space_data.region_3d.
    :type rv3d: :class:`bpy.types.RegionView3D`
    :arg coord: 2d coordinates relative to the region:
       (event.mouse_region_x, event.mouse_region_y) for example.
    :type coord: 2d vector
    :return: normalized 3d vector.
    :rtype: :class:`mathutils.Vector`
    """
    from mathutils import Vector

    viewinv = rv3d.view_matrix.inverted()
    if rv3d.is_perspective:
        persinv = rv3d.perspective_matrix.inverted()

        out = Vector(((2.0 * coord[0] / region.width) - 1.0,
                      (2.0 * coord[1] / region.height) - 1.0,
                      -0.5
                     ))

        w = out.dot(persinv[3].xyz) + persinv[3][3]

        return ((persinv * out) / w) - viewinv.translation
    else:
        return viewinv.col[2].xyz.normalized()
Esempio n. 15
0
def signed_area(verts, normal):
    total = Vector((0, 0, 0))
    l = len(verts)
    for i in range(0, l):
        total += verts[i].cross(verts[(i+1) % l])
    result = total.dot(normal)
    return result / 2
Esempio n. 16
0
    def by_edge_dir(self, vertices, edges, faces):
        percent = self.inputs['Percent'].sv_get(default=[1.0])[0][0]
        direction = self.inputs['Direction'].sv_get()[0][0]
        dirvector = Vector(direction)
        dirlength = dirvector.length
        if dirlength <= 0:
            raise ValueError("Direction vector must have nonzero length!")

        values = []
        for i, j in edges:
            u = vertices[i]
            v = vertices[j]
            edge = Vector(u) - Vector(v)
            if edge.length > 0:
                value = abs(edge.dot(dirvector)) / (edge.length * dirlength)
            else:
                value = 0
            values.append(value)
        threshold = self.map_percent(values, percent)
    
        out_edges_mask = [(value >= threshold) for value in values]
        out_edges = [edge for (edge, mask) in zip (edges, out_edges_mask) if mask]
        out_verts_mask = self.select_verts_by_faces(out_edges, vertices)
        out_faces_mask = self.select_faces_by_verts(out_verts_mask, faces)

        return out_verts_mask, out_edges_mask, out_faces_mask
Esempio n. 17
0
def region_2d_to_vector_3d(region, rv3d, coord):
    """
    Return a direction vector from the viewport at the specific 2d region
    coordinate.

    :arg region: region of the 3D viewport, typically bpy.context.region.
    :type region: :class:`bpy.types.Region`
    :arg rv3d: 3D region data, typically bpy.context.space_data.region_3d.
    :type rv3d: :class:`bpy.types.RegionView3D`
    :arg coord: 2d coordinates relative to the region:
       (event.mouse_region_x, event.mouse_region_y) for example.
    :type coord: 2d vector
    :return: normalized 3d vector.
    :rtype: :class:`mathutils.Vector`
    """
    from mathutils import Vector

    viewinv = rv3d.view_matrix.inverted()
    if rv3d.is_perspective:
        persinv = rv3d.perspective_matrix.inverted()
        width = region.width
        height = region.height

        out = Vector(((2.0 * coord[0] / width) - 1.0,
                      (2.0 * coord[1] / height) - 1.0, -0.5))

        w = out.dot(persinv[3].xyz) + persinv[3][3]

        view_vector = ((persinv @ out) / w) - viewinv.translation
    else:
        view_vector = -viewinv.col[2].xyz

    view_vector.normalize()

    return view_vector
Esempio n. 18
0
def vector_to_mesh(vector,
                   name='vector',
                   width=1e-2,
                   frac_length_head=0.2,
                   frac_width_head=2,
                   n=20):
    w = Vector(vector)
    length = w.length
    w.normalize()
    u = Vector((0, 0, 0))
    i = numpy.argmin(numpy.absolute(vector))
    u[i] = 1
    u = (u - u.dot(w) * w).normalized()
    v = w.cross(u)
    #
    t = numpy.linspace(0, 2 * numpy.pi, n + 1)
    t = t[:n]
    #
    verts = [(width * (1 + frac_width_head * max(0, j - 1)) *
              (numpy.cos(t[i]) * u + numpy.sin(t[i]) * v) + length *
              (1 - frac_length_head) * min(1, j) * w) for j in range(3)
             for i in range(n)]
    verts.append((length * w))
    #
    faces = [(j * n + i, j * n + (i + 1) % n, (j + 1) * n + (i + 1) % n,
              (j + 1) * n + i) for j in range(2) for i in range(n)]
    faces.extend([(3 * n, 2 * n + i, 2 * n + (i + 1) % n) for i in range(n)])
    #
    return pydata_to_mesh(verts, faces, edges=[], name=name)
Esempio n. 19
0
        def generate_perpendicular_bone_direction(this_bone_matrix: Matrix,
                                                  parent_dir: Vector):
            # Pick a vector that's sort of in the same direction we want the bone to point in
            # (i.e. we don't want the bone to go in/out, so don't pick (0, 0, 1))
            target_dir = Vector((0, 1, 0))
            if abs(parent_dir.dot(target_dir)) > 0.99:
                # Parent and proposed perpendicular direction are basically the same axis, cross product won't work
                # Choose a different one
                target_dir = Vector((1, 0, 0))

            # parent_dir cross target_dir creates a vector that's guaranteed to be perpendicular to both of them.
            perp_dir = parent_dir.cross(target_dir).normalized()
            print(f"{parent_dir} X {target_dir} = {perp_dir}")

            # Then, parent_dir cross perp_dir will create a vector that is both
            #   1) perpendicular to parent_dir
            #   2) in the same sort of direction as target_dir
            # use this vector as our tail_delta
            tail_delta_dir = parent_dir.cross(perp_dir).normalized()
            print(f"{parent_dir} X {perp_dir} = {tail_delta_dir}")

            # Cross product can have bad symmetry - bones on opposite sides of the skeleton can get deltas that look weird
            # Fix this by picking the delta which moves the tail the farthest possible distance from the origin
            # This will choose consistent directions regardless of which side of the vertical axis you are on
            distance_from_origin_with_positive = (
                this_bone_matrix @ (tail_delta_dir * 0.1)).length
            distance_from_origin_with_negative = (
                this_bone_matrix @ (-tail_delta_dir * 0.1)).length
            if distance_from_origin_with_negative > distance_from_origin_with_positive:
                tail_delta_dir = -tail_delta_dir

            return tail_delta_dir
Esempio n. 20
0
def region_2d_to_orig_and_view_vector(region, rv3d, coord):
    viewinv = rv3d.view_matrix.inverted()
    persinv = rv3d.perspective_matrix.inverted()

    x, y = region.width, region.height

    dx = (2.0 * coord[0] / x) - 1.0
    dy = (2.0 * coord[1] / y) - 1.0

    if rv3d.is_perspective:
        origin_start = viewinv.translation.copy()

        out = Vector((dx, dy, -0.5))

        w = out.dot(persinv[3].xyz) + persinv[3][3]

        view_vector = ((persinv @ out) / w) - origin_start
    else:
        view_vector = -viewinv.col[2].xyz

        origin_start = ((persinv.col[0].xyz * dx) +
                        (persinv.col[1].xyz * dy) +
                        viewinv.translation)
        # S.L in ortho view, origin may be plain wrong so add arbitrary distance ..
        origin_start -= view_vector * 1000

    view_vector.normalize()
    return view_vector, origin_start
Esempio n. 21
0
def _is_flat_face(normal):
    a = Vector(normal[0])
    for n in normal[1:]:
        dp = a.dot(Vector(n))
        if dp < 0.99999 or dp > 1.00001:
            return False
    return True
def _is_flat_face(normal):
    a = Vector(normal[0])
    for n in normal[1:]:
        dp = a.dot(Vector(n))
        if dp < 0.99999 or dp > 1.00001:
            return False
    return True
Esempio n. 23
0
    def _region_2d_to_orig_and_vect(self, coord, clamp=None):

        viewinv = self._rv3d.view_matrix.inverted()
        persinv = self._rv3d.perspective_matrix.inverted()
        dx = (2.0 * coord[0] / self._region.width) - 1.0
        dy = (2.0 * coord[1] / self._region.height) - 1.0

        if self._rv3d.is_perspective:
            origin_start = viewinv.translation.copy()
            out = Vector((dx, dy, -0.5))
            w = out.dot(persinv[3].xyz) + persinv[3][3]
            view_vector = ((persinv @ out) / w) - origin_start

        else:
            view_vector = -viewinv.col[2].xyz
            origin_start = ((persinv.col[0].xyz * dx) +
                            (persinv.col[1].xyz * dy) + persinv.translation)
            if self._rv3d.view_perspective != 'CAMERA':
                # this value is scaled to the far clip already
                origin_offset = persinv.col[2].xyz
                if clamp is not None:
                    # Since far clip can be a very large value,
                    # the result may give with numeric precision issues.
                    # To avoid this problem, you can optionally clamp the far clip to a
                    # smaller value based on the data you're operating on.
                    if clamp < 0.0:
                        origin_offset.negate()
                        clamp = -clamp
                    if origin_offset.length > clamp:
                        origin_offset.length = clamp
                origin_start -= origin_offset
        view_vector.normalize()
        return origin_start, view_vector
Esempio n. 24
0
def castRays(obj: Object,
             point: Vector,
             direction: Vector,
             miniDist: float,
             roundType: str = "CEILING",
             edgeLen: int = 0):
    """
    obj       -- source object to test intersections for
    point     -- origin point for ray casting
    direction -- cast ray in this direction
    miniDist  -- Vector with miniscule amount to add after intersection
    roundType -- round final intersection location Vector with this type
    edgeLen   -- distance to test for intersections
    """
    # initialize variables
    firstDirection = False
    firstIntersection = None
    nextIntersection = None
    lastIntersection = None
    edgeIntersects = False
    edgeLen2 = edgeLen * 1.00001
    orig = point
    intersections = 0
    # cast rays until no more rays to cast
    while True:
        _, location, normal, index = obj.ray_cast(
            orig, direction)  #distance=edgeLen*1.00000000001)
        if index == -1: break
        if intersections == 0:
            firstDirection = direction.dot(normal)
        if edgeLen != 0:
            dist = (location - point).length
            # get first and last intersection (used when getting materials of nearest (first or last intersected) face)
            if dist <= edgeLen2:
                if intersections == 0:
                    edgeIntersects = True
                    firstIntersection = {
                        "idx": index,
                        "dist": dist,
                        "loc": location,
                        "normal": normal
                    }
                lastIntersection = {
                    "idx": index,
                    "dist": edgeLen - dist,
                    "loc": location,
                    "normal": normal
                }

            # set nextIntersection
            if nextIntersection is None:
                nextIntersection = location.copy()
        intersections += 1
        location = VectorRound(location, 5, roundType=roundType)
        orig = location + miniDist

    if edgeLen != 0:
        return intersections, firstDirection, firstIntersection, nextIntersection, lastIntersection, edgeIntersects
    else:
        return intersections, firstDirection
Esempio n. 25
0
def vec_roll_to_mat3(axis, roll):
    """Computes 3x3 Matrix from rotation axis and its roll.

    :param axis: Rotation
    :type axis: Vector
    :param roll: Roll
    :type roll: float
    :return: 3x3 Matrix
    :rtype: Matrix
    """
    nor = axis.normalized()
    target = Vector((0, 1, 0))
    axis = target.cross(nor)

    if axis.dot(axis) > 1.0e-9:
        axis.normalize()
        theta = _math_utils.angle_normalized_v3v3(target, nor)
        b_matrix = Matrix.Rotation(theta, 4, axis)
    else:
        if target.dot(nor) > 0:
            up_or_down = 1.0
        else:
            up_or_down = -1.0

        b_matrix = Matrix()
        b_matrix[0] = (up_or_down, 0, 0, 0)
        b_matrix[1] = (0, up_or_down, 0, 0)
        b_matrix[2] = (0, 0, 1, 0)
        b_matrix[3] = (0, 0, 0, 1)

    roll_matrix = Matrix.Rotation(roll, 4, nor)
    return (roll_matrix * b_matrix).to_3x3()
Esempio n. 26
0
def points_inside(points, bm):
    """
    https://blender.stackexchange.com/questions/31693/how-to-find-if-a-point-is-inside-a-mesh
    input:
        points
        - a list of vectors (can also be tuples/lists)
        bm
        - a manifold bmesh with verts and (edge/faces) for which the
          normals are calculated already. (add bm.normal_update() otherwise)
    returns:
        a list
        - a mask lists with True if the point is inside the bmesh, False otherwise
    """
    rpoints = []
    addp = rpoints.append
    bvh = BVHTree.FromBMesh(bm, epsilon=0.0001)

    # return points on polygons
    for point in points:
        fco, normal, _, _ = bvh.find_nearest(point)
        # calcula vector
        #p2 = fco - Vector(point)
        p2 = Vector(point) - fco
        # Si el producto escalar es negativo, "están en direccion opuesta"
        v = p2.dot(normal)
        addp(v < 0.0)  # addp(v >= 0.0) ?

    return rpoints
Esempio n. 27
0
def vec_roll_to_mat3(axis, roll):
    """Computes 3x3 Matrix from rotation axis and its roll.

    :param axis: Rotation
    :type axis: Vector
    :param roll: Roll
    :type roll: float
    :return: 3x3 Matrix
    :rtype: Matrix
    """
    nor = axis.normalized()
    target = Vector((0, 1, 0))
    axis = target.cross(nor)

    if axis.dot(axis) > 1.0e-9:
        axis.normalize()
        theta = _math_utils.angle_normalized_v3v3(target, nor)
        b_matrix = Matrix.Rotation(theta, 4, axis)
    else:
        if target.dot(nor) > 0:
            up_or_down = 1.0
        else:
            up_or_down = -1.0

        b_matrix = Matrix()
        b_matrix[0] = (up_or_down, 0, 0, 0)
        b_matrix[1] = (0, up_or_down, 0, 0)
        b_matrix[2] = (0, 0, 1, 0)
        b_matrix[3] = (0, 0, 0, 1)

    roll_matrix = Matrix.Rotation(roll, 4, nor)
    return (roll_matrix * b_matrix).to_3x3()
def coplanar_pols(val, orig_val, threshold):
    equal_normals = equal_vectors(val[0], orig_val[0], threshold)
    if equal_normals:
        vector_base = Vector(val[1])- Vector(orig_val[1])
        distance = vector_base.dot(Vector(val[0]))
        return abs(distance) < threshold
    else:
        return False
Esempio n. 29
0
def mesh_diffusion(me, values, iter, diff=0.2, uv_dir=0):
    values = np.array(values)
    n_verts = len(me.vertices)

    n_edges = len(me.edges)
    edge_verts = [0]*n_edges*2
    #me.edges.foreach_get("vertices", edge_verts)

    count = 0
    edge_verts = []
    uv_factor = {}
    uv_ang = (0.5 + uv_dir*0.5)*pi/2
    uv_vec = Vector((cos(uv_ang), sin(uv_ang)))
    for i, f in enumerate(me.polygons):
        f_verts = len(f.vertices)
        for j0 in range(f_verts):
            j1 = (j0+1)%f_verts
            if uv_dir != 0:
                uv0 = me.uv_layers[0].data[count+j0].uv
                uv1 = me.uv_layers[0].data[count+j1].uv
                delta_uv = (uv1-uv0).normalized()
                delta_uv.x = abs(delta_uv.x)
                delta_uv.y = abs(delta_uv.y)
                dir = uv_vec.dot(delta_uv)
            else:
                dir = 1
            #dir = abs(dir)
            #uv_factor.append(dir)
            edge_key = [f.vertices[j0], f.vertices[j1]]
            edge_key.sort()
            uv_factor[tuple(edge_key)] = dir
        count += f_verts
    id0 = []
    id1 = []
    uv_mult = []
    for ek, val in uv_factor.items():
        id0.append(ek[0])
        id1.append(ek[1])
        uv_mult.append(val)
    id0 = np.array(id0)
    id1 = np.array(id1)
    uv_mult = np.array(uv_mult)

    #edge_verts = np.array(edge_verts)
    #arr = np.arange(n_edges)*2

    #id0 = edge_verts[arr]     # first vertex indices for each edge
    #id1 = edge_verts[arr+1]   # second vertex indices for each edge
    for ii in range(iter):
        lap = np.zeros(n_verts)
        if uv_dir != 0:
            lap0 =  (values[id1] -  values[id0])*uv_mult   # laplacian increment for first vertex of each edge
        else:
            lap0 =  (values[id1] -  values[id0])
        np.add.at(lap, id0, lap0)
        np.add.at(lap, id1, -lap0)
        values += diff*lap
    return values
def random_axes_from_normal(z):
    Z = z.normalized()
    x = Vector((random.random(), random.random(), random.random()))
    X = x - x.dot(Z) * Z
    X.normalize()

    Y = Z.cross(X)

    return X, Y, Z
Esempio n. 31
0
def polygon_normal_angle_D(verts, poly, D):
    ''' The angle between the polygon normal and the given direction '''
    N = polygon_normal(verts, poly)
    v1 = Vector(N)
    v2 = Vector(D)
    v1.normalize()
    v2.normalize()
    angle = acos(v1.dot(v2)) # the angle in radians

    return angle
Esempio n. 32
0
def polygon_normal_angle_D(verts, poly, D):
    ''' The angle between the polygon normal and the given direction '''
    N = polygon_normal(verts, poly)
    v1 = Vector(N)
    v2 = Vector(D)
    v1.normalize()
    v2.normalize()
    angle = acos(v1.dot(v2))  # the angle in radians

    return angle
Esempio n. 33
0
def reciprocal_conversion(a1, a2, a3, b1, b2, b3):
    a1 = Vector(a1)
    a2 = Vector(a2)
    a3 = Vector(a3)
    vol = a1.dot(a2.cross(a3))
    if vol != 0:
        b1[:] = a2.cross(a3) / vol
        b2[:] = a3.cross(a1) / vol
        b3[:] = a1.cross(a2) / vol
    return vol
Esempio n. 34
0
def AdjustRoll_axisplane(bone, nor):
    props = bpy.context.scene.cyarigtools_props
    mat = bone.matrix

    z = Vector((mat[0][2], mat[1][2], mat[2][2]))
    z.normalize()

    #Xvectorを回転の正負判定に使う
    #X軸と法線の内積が正なら+、負ならー
    x = Vector((mat[0][0], mat[1][0], mat[2][0]))
    sign = x.dot(nor) / math.fabs(x.dot(nor))

    cos_sita = z.dot(nor)
    sita = math.acos(cos_sita)

    if props.axis_plane == 'Z':
        bone.roll = sita * sign

    elif props.axis_plane == 'X':
        bone.roll = sita * sign + math.pi / 2
Esempio n. 35
0
    def handle_mouse_move(self, context, mouse_x, mouse_y):
        """
        Update the gizmo for current mouse position if necessary.
        Otherwise remove the gizmo.
        """

        if self.forward is not None:
            self.log.info("forward is not none")
            relative_mouse_pos = Vector((mouse_x, mouse_y)) - self.origin_2d
            t = relative_mouse_pos.dot(self.origin_normal_2d)
            projected = self.origin_2d + t * self.origin_normal_2d

            p_x = projected[0]
            p_y = projected[1]

            relative_mouse_pos = Vector((mouse_x, mouse_y)) - self.origin_2d
            t = relative_mouse_pos.dot(self.origin_normal_2d)
            projected = self.origin_2d + t * self.origin_normal_2d

            p_x = projected[0]
            p_y = projected[1]

            region = context.region
            region_data = context.space_data.region_3d

            view_vector = bpy_extras.view3d_utils.region_2d_to_vector_3d(
                region, region_data, (p_x, p_y))
            ray_origin = bpy_extras.view3d_utils.region_2d_to_origin_3d(
                region, region_data, (p_x, p_y))

            ray_target = ray_origin + view_vector

            points = mathutils.geometry.intersect_line_line(
                self.origin, self.origin + self.origin_normal, ray_origin,
                ray_target)

            self.height = (points[0] - self.origin).length

        self.refresh_preview(context, mouse_x, mouse_y)

        return {'RUNNING_MODAL'}
Esempio n. 36
0
def polygon_normal_angle_P(verts, poly, P):
    ''' The angle between the polygon normal and the vector from polygon center to given point '''
    N = polygon_normal(verts, poly)
    C = polygon_center(verts, poly)
    V = [P[0] - C[0], P[1] - C[1], P[2] - C[2]]
    v1 = Vector(N)
    v2 = Vector(V)
    v1.normalize()
    v2.normalize()
    angle = acos(v1.dot(v2)) # the angle in radians

    return angle
Esempio n. 37
0
def vec_roll_to_mat3(vec, roll):
	target = Vector((0,1,0))
	nor = vec.normalized()
	axis = target.cross(nor)
	if axis.dot(axis) > 0.000001:
		axis.normalize()
		theta = target.angle(nor)
		bMatrix = Matrix.Rotation(theta, 3, axis)
	else:
		updown = 1 if target.dot(nor) > 0 else -1
		bMatrix = Matrix.Scale(updown, 3)
	rMatrix = Matrix.Rotation(roll, 3, nor)
	mat = rMatrix * bMatrix
	return mat
Esempio n. 38
0
def polygon_area(verts, poly):
    ''' The area of the given polygon '''
    if len(poly) < 3:  # not a plane - no area
        return 0

    total = Vector([0, 0, 0])
    N = len(poly)
    for i in range(N):
        vi1 = Vector(verts[poly[i]])
        vi2 = Vector(verts[poly[(i + 1) % N]])
        prod = vi1.cross(vi2)
        total[0] += prod[0]
        total[1] += prod[1]
        total[2] += prod[2]

    normal = Vector(polygon_normal(verts, poly))
    area = abs(total.dot(normal)) / 2

    return area
Esempio n. 39
0
File: fkik.py Progetto: jultrunb/ass
def matchIkLeg(legIk, toeFk, mBall, mToe, mHeel):
    rmat = toeFk.matrix.to_3x3()
    tHead = Vector(toeFk.matrix.col[3][:3])
    ty = rmat.col[1]
    tail = tHead + ty * toeFk.bone.length

    zBall = mBall.matrix.col[3][2]
    zToe = mToe.matrix.col[3][2]
    zHeel = mHeel.matrix.col[3][2]

    x = Vector(rmat.col[0])
    y = Vector(rmat.col[1])
    z = Vector(rmat.col[2])

    if zHeel > zBall and zHeel > zToe:
        # 1. foot.ik is flat
        if abs(y[2]) > abs(z[2]):
            y = -z
        y[2] = 0
    else:
        # 2. foot.ik starts at heel
        hHead = Vector(mHeel.matrix.col[3][:3])
        y = tail - hHead

    y.normalize()
    x -= x.dot(y)*y
    x.normalize()
    if abs(x[2]) < 0.7:
        x[2] = 0
        x.normalize()
    z = x.cross(y)
    head = tail - y * legIk.bone.length

    # Create matrix
    gmat = Matrix()
    gmat.col[0][:3] = x
    gmat.col[1][:3] = y
    gmat.col[2][:3] = z
    gmat.col[3][:3] = head
    pmat = getPoseMatrix(gmat, legIk)

    insertLocation(legIk, pmat)
    insertRotation(legIk, pmat)
Esempio n. 40
0
def area_pol(poly):
    if len(poly) < 3:  # not a plane - no area
        return 0

    total = Vector((0, 0, 0))
    for i in range(len(poly)):
        vi1 = Vector(poly[i])
        if i is len(poly)-1:
            vi2 = Vector(poly[0])
        else:
            vi2 = Vector(poly[i+1])

        prod = vi1.cross(vi2)[:]
        total[0] += prod[0]
        total[1] += prod[1]
        total[2] += prod[2]

    result = total.dot(unit_normal(poly[0], poly[1], poly[2]))
    return abs(result/2)
def region_2d_to_orig_and_view_vector(region, rv3d, coord, clamp=None):
    viewinv = rv3d.view_matrix.inverted()
    persinv = rv3d.perspective_matrix.inverted()

    dx = (2.0 * coord[0] / region.width) - 1.0
    dy = (2.0 * coord[1] / region.height) - 1.0

    if rv3d.is_perspective:
        origin_start = viewinv.translation.copy()

        out = Vector((dx, dy, -0.5))

        w = out.dot(persinv[3].xyz) + persinv[3][3]

        view_vector = ((persinv * out) / w) - origin_start
    else:
        view_vector = -viewinv.col[2].xyz

        origin_start = ((persinv.col[0].xyz * dx) +
                        (persinv.col[1].xyz * dy) +
                        viewinv.translation)

        if clamp != 0.0:
            if rv3d.view_perspective != 'CAMERA':
                # this value is scaled to the far clip already
                origin_offset = persinv.col[2].xyz
                if clamp is not None:
                    if clamp < 0.0:
                        origin_offset.negate()
                        clamp = -clamp
                    if origin_offset.length > clamp:
                        origin_offset.length = clamp

                origin_start -= origin_offset

    view_vector.normalize()
    return origin_start, view_vector
Esempio n. 42
0
def region_2d_to_orig_and_view_vector(region, rv3d, coord):
    viewinv = rv3d.view_matrix.inverted_safe()
    persinv = rv3d.perspective_matrix.inverted_safe()

    dx = (2.0 * coord[0] / region.width) - 1.0
    dy = (2.0 * coord[1] / region.height) - 1.0

    if rv3d.is_perspective:
        origin_start = viewinv.translation.copy()

        out = Vector((dx, dy, -0.5))

        w = out.dot(persinv[3].xyz) + persinv[3][3]

        view_vector = ((persinv @ out) / w) - origin_start
    else:
        view_vector = -viewinv.col[2].xyz

        origin_start = ((persinv.col[0].xyz * dx) +
                        (persinv.col[1].xyz * dy) +
                        viewinv.translation)

    view_vector.normalize()
    return view_vector, origin_start
Esempio n. 43
0
def _arc_segment(v_1, v_2):
	ELorigin = bpy.context.scene.objects['ELorigin']
	ELground = bpy.context.scene.objects['ELground']

	v = v_2 - v_1
	d = v.length

	ELorigin.location = Vector((0, 0, 0))
	ELground.location = Vector((0, 0, -d))

	v_L = ELground.location - ELorigin.location

	q = Quaternion()
	c = Vector.cross(v_L, v)
	q.x = c.x
	q.y = c.y
	q.z = c.z
	q.w = sqrt((v_L.length ** 2) * (v.length ** 2)) + \
		Vector.dot(v_L, v)
	q.normalize()
	euler = q.to_euler()

	bpy.ops.object.runfslg_operator()

	laALL = bpy.context.scene.objects['laALL']
	laALL.name = 'lARC'
	laALL.rotation_euler = euler
	laALL.location = v_1

	bpy.context.active_object.select = False
	laALL.select = True
	bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
	laALL.select = False
	bpy.context.active_object.select = True	

	return laALL
Esempio n. 44
0
 def __new__(cls, location=Vector(), normal=ZAXIS, rotation=Quaternion()):
     loc = Vector(location)
     nor = Vector(normal).normalized()
     vector = nor.to_4d()
     vector[3] = -nor.dot(loc)
     return Vector.__new__(cls, vector)
def draw_molecule(molecule, center=(0, 0, 0), max_molecule_size=5,
                  show_bonds=True):
    """Draw a molecule to blender. Uses loaded json molecule data."""

    # Get scale factor - only scales large molecules down
#    max_coord = 1E-6
#    for atom in molecule["atoms"]:
#        max_coord = max(max_coord, *[abs(a) for a in atom["location"]])
#    scale = min(max_molecule_size / max_coord, 1)
    scale = 0.1 # if one gets the atomic positions through spd files, they are denoted in \AA, switch to nm => /10 in location

    # Scale location coordinates and add specified center
    for atom in molecule["atoms"]:
        atom["location"] = [c + x * scale for c, x in zip(center,
                                                          atom["location"])]
    draw_vdW = True
    join = False
    # Keep references to all atoms and bonds
    shapes = []

    # If using space-filling model, scale up atom size and remove bonds
    if show_bonds:
        bond_scale = 0.1
        atom_scale = 0.5
    else:
        atom_scale = 1
        bond_scale = 0.2
        molecule["bonds"] = []

    # Add atom primitive
    bpy.ops.object.select_all(action='DESELECT')
    bpy.ops.mesh.primitive_uv_sphere_add()
    sphere = bpy.context.object

    # Add bond material and primitive if it's going to be used
    if molecule["bonds"]:
        key = "bond"
        bpy.data.materials.new(name=key)
        bpy.data.materials[key].diffuse_color = atom_data[key]["color"]
        bpy.data.materials[key].specular_intensity = 0.2
        bpy.ops.mesh.primitive_cylinder_add()
        cylinder = bpy.context.object
        cylinder.active_material = bpy.data.materials["bond"]

    # Draw atoms
    for atom in molecule["atoms"]:

        # If element is not in dictionary, use undefined values
        if atom["element"] not in atom_data:
            atom["element"] = "undefined"

        # If material for atom type has not yet been defined, do so
        if atom["element"] not in bpy.data.materials:
            key = atom["element"]
            print("added material", key, "to materials")
            bpy.data.materials.new(name=key)
            bpy.data.materials[key].diffuse_color = atom_data[key]["color"]
            bpy.data.materials[key].specular_intensity = 0.2
            #add vdW spheres to atoms 
            if draw_vdW:           
                keyvdW = atom["element"] + "-vdW"
                bpy.data.materials.new(name=keyvdW)
                bpy.data.materials[keyvdW].diffuse_color = atom_data[key]["color"]	#same color as atom, but with tranparancy
                bpy.data.materials[keyvdW].use_transparency = 1
                bpy.data.materials[keyvdW].transparency_method = "RAYTRACE"
                bpy.data.materials[keyvdW].alpha=0.3
                print("added vdW material", key, "to materials")

        # Copy mesh primitive and edit to make atom
        atom_sphere = sphere.copy()
        atom_sphere.data = sphere.data.copy()
        atom_sphere.location = atom["location"]
        atom_sphere.dimensions = [atom_data[atom["element"]]["radius"] *
                                  atom_scale * 2] * 3
        atom_sphere.active_material = bpy.data.materials[atom["element"]]
        bpy.context.scene.objects.link(atom_sphere)
        shapes.append(atom_sphere)
        keyvdW = atom["element"] + "-vdW"
        if draw_vdW:
            atom_sphere = sphere.copy()
            atom_sphere.data = sphere.data.copy()
            atom_sphere.location = atom["location"]
            atom_sphere.dimensions = [atom_data[atom["element"]]["vdWradius"] * atom_scale] * 3
            atom_sphere.active_material = bpy.data.materials[keyvdW]
            bpy.context.scene.objects.link(atom_sphere)
            shapes.append(atom_sphere)

    # Draw bonds
    for bond in molecule["bonds"]:

        # Extracting locations
        first_loc = molecule["atoms"][bond["atoms"][0]]["location"]
        second_loc = molecule["atoms"][bond["atoms"][1]]["location"]
        diff = [c2 - c1 for c2, c1 in zip(first_loc, second_loc)]
        cent = [(c2 + c1) / 2 for c2, c1 in zip(first_loc, second_loc)]
        mag = sum([(c2 - c1) ** 2
                   for c1, c2 in zip(first_loc, second_loc)]) ** 0.5

        # Euler rotation calculation
        v_axis = Vector(diff).normalized()
        v_obj = Vector((0, 0, 1))
        v_rot = v_obj.cross(v_axis)

        # This check prevents gimbal lock (ie. weird behavior when v_axis is
        # close to (0, 0, 1))
        if v_rot.length > 0.01:
            v_rot = v_rot.normalized()
            axis_angle = [acos(v_obj.dot(v_axis))] + list(v_rot)
        else:
            v_rot = Vector((1, 0, 0))
            axis_angle = [0] * 4

        # Check that the number of bonds is logical
        if bond["order"] not in range(1, 4):
            print("Improper number of bonds! Defaulting to 1.")
            bond["order"] = 1

        # Specify locations of each bond in every scenario
        if bond["order"] == 1:
            trans = [[0] * 3]
        elif bond["order"] == 2:
            trans = [[1.4 * atom_data["bond"]["radius"] * x for x in v_rot],
                     [-1.4 * atom_data["bond"]["radius"] * x for x in v_rot]]
        elif bond["order"] == 3:
            trans = [[0] * 3,
                     [2.2 * atom_data["bond"]["radius"] * x for x in v_rot],
                     [-2.2 * atom_data["bond"]["radius"] * x for x in v_rot]]
        # Draw bonds
        for i in range(bond["order"]):
            bond_cylinder = cylinder.copy()
            bond_cylinder.data = cylinder.data.copy()
            bond_cylinder.dimensions = [atom_data["bond"]["radius"] * bond_scale] * 2 + [mag]
            bond_cylinder.location = [c + scale * v for c,
                                      v in zip(cent, trans[i])]
            bond_cylinder.rotation_mode = "AXIS_ANGLE"
            bond_cylinder.rotation_axis_angle = axis_angle
            bpy.context.scene.objects.link(bond_cylinder)
            shapes.append(bond_cylinder)

    # Remove primitive meshes
    bpy.ops.object.select_all(action='DESELECT')
    sphere.select = True
    if molecule["bonds"]:
        cylinder.select = True
    # If the starting cube is there, remove it
    if "Cube" in bpy.data.objects.keys():
        bpy.data.objects.get("Cube").select = True
    bpy.ops.object.delete()

    # Smooth and join molecule shapes
    for shape in shapes:
        shape.select = True
    bpy.context.scene.objects.active = shapes[0]
    bpy.ops.object.shade_smooth()
    if join: 
        bpy.ops.object.join()
# Center object origin to geometry    
        bpy.ops.object.origin_set(type="ORIGIN_GEOMETRY", center="MEDIAN")
    


    # Refresh scene
    bpy.context.scene.update()
Esempio n. 46
0
    def setupJoints (self):
        """
        Evaluate symbolic expressions for joint locations and store them in self.locations.
        Joint locations are specified symbolically in the *Joints list in the beginning of the
        rig_*.py files (e.g. ArmJoints in rig_arm.py).
        """

        cfg = self.config
        for (key, type, data) in self.joints:
            if type == 'j':
                loc = self.jointLocs[data]
                self.locations[key] = loc
                self.locations[data] = loc
            elif type == 'a':
                vec = Vector((float(data[0]),float(data[1]),float(data[2])))
                self.locations[key] = vec + self.offset
            elif type == 'v':
                v = int(data)
                self.locations[key] = self.coord[v]
            elif type == 'x':
                self.locations[key] = Vector((float(data[0]), float(data[2]), -float(data[1])))
            elif type == 'vo':
                v = int(data[0])
                offset = Vector((float(data[1]), float(data[3]), -float(data[2])))
                self.locations[key] = (self.coord[v] + self.scale*offset)
            elif type == 'vl':
                ((k1, v1), (k2, v2)) = data
                loc1 = self.coord[int(v1)]
                loc2 = self.coord[int(v2)]
                self.locations[key] = (k1*loc1 + k2*loc2)
            elif type == 'f':
                (raw, head, tail, offs) = data
                rloc = self.locations[raw]
                hloc = self.locations[head]
                tloc = self.locations[tail]
                vec = tloc - hloc
                vraw = rloc - hloc
                x = vec.dot(vraw)/vec.dot(vec)
                self.locations[key] = hloc + x*vec + Vector(offs)
            elif type == 'n':
                (raw, j1, j2, j3) = data
                rloc = self.locations[raw]
                loc1 = self.locations[j1]
                loc2 = self.locations[j2]
                loc3 = self.locations[j3]
                e12 = loc2 - loc1
                e13 = loc3 - loc1
                n = e12.cross(e13).normalized()
                e1r = rloc - loc1
                self.locations[key] = rloc - n*e1r.dot(n)
            elif type == 'b':
                self.locations[key] = self.jointLocs[key] = self.locations[data]
            elif type == 'p':
                x = self.locations[data[0]]
                y = self.locations[data[1]]
                z = self.locations[data[2]]
                self.locations[key] = Vector((x[0],y[1],z[2]))
            elif type == 'vz':
                v = int(data[0])
                z = self.coord[v][2]
                loc = self.locations[data[1]]
                self.locations[key] = Vector((loc[0],loc[1],z))
            elif type == 'X':
                r = self.locations[data[0]]
                (x,y,z) = data[1]
                r1 = Vector([float(x), float(y), float(z)])
                self.locations[key] = r.cross(r1)
            elif type == 'l':
                ((k1, joint1), (k2, joint2)) = data
                self.locations[key] = k1*self.locations[joint1] + k2*self.locations[joint2]
            elif type == 'o':
                (joint, offsSym) = data
                if isinstance(offsSym, str):
                    offs = self.locations[offsSym]
                else:
                    offs = self.scale * Vector(offsSym)
                self.locations[key] = self.locations[joint] + offs
            else:
                raise NameError("Unknown %s" % type)
        return
Esempio n. 47
0
	def execute(self, context):
		region = getActiveRegion3d()
		active_obj = context.scene.objects.active
		select_and_change_mode(active_obj, 'EDIT')
		edit_obj = bpy.context.edit_object
		active_mesh = edit_obj.data
		bm = bmesh.from_edit_mesh(active_mesh)
		bm.verts.ensure_lookup_table()
		bm.faces.ensure_lookup_table()
		bm.verts.index_update()
		# Get all selected vertices (in their local space).
		selectedVerts = [v for v in bm.verts if v.select]
		if len(selectedVerts) < 3:
			self.report({'ERROR'}, "Not enough selected vertices found")
			return {'CANCELLED'}
		stroke2dPoints, stroke3dPoints = gpencil_to_screenpos(context, g_strokeResl, region)
		if len(stroke2dPoints) < 3:
			self.report({'ERROR'}, "Grease pencil stroke too short or not found")
			return {'CANCELLED'}

		#verts_global_3d = [Vector(obj.matrix_world * v.co) for v in selectedVerts]
		eo_mx_inv = edit_obj.matrix_world.inverted()
		eo_dir2cam = eo_mx_inv * (region.view_rotation * Vector((0.0, 0.0, 1.0)))
		eo_dir2cam = Vector(eo_dir2cam).normalized()
		#eo_mx_norm = eo_mx_inv.transposed().to_3x3()
		verts3dPoints = [v.co for v in selectedVerts]
		verts2dPoints = vectors_to_screenpos(context, verts3dPoints, edit_obj.matrix_world, region)
		vertsFieldDispersion, nearestStrokeInPoints = getVertsFD(verts2dPoints, verts3dPoints, stroke2dPoints, self.FacMid, self.FacPits, self.SlopePits, self.inpaintStrk)
		shiftDir = eo_dir2cam
		if self.push_trg == 'AVG_NORMLS':
			# points most near to begin/end of stroke to get movement vector
			normalsSumm = g_zeroVec.copy()
			normalsCount = 0
			for i, v in enumerate(verts2dPoints):
				vnrm = (selectedVerts[i].normal).normalized() #(eo_mx_norm*selectedVerts[i].normal).normalized()
				normalsSumm = normalsSumm+vnrm*vertsFieldDispersion[i]
				normalsCount = normalsCount+1
			shiftDir = normalsSumm/normalsCount
		if self.push_trg == 'LST_NORMLS':
			lastv = getLastSelectedVert(bm)
			if lastv is not None:
				shiftDir = (lastv.normal).normalized() #(eo_mx_norm*lastvn).normalized()
		for i, v in enumerate(selectedVerts):
			fac = 1.0
			if self.invertFld:
				fac = self.influence*(1.0-vertsFieldDispersion[i])
			else:
				fac = self.influence*vertsFieldDispersion[i]
			if self.limitFld2NV:
				fac = fac*eo_dir2cam.dot(v.normal)
			if fac is not None:
				if self.push_trg == 'ROTATE_CENTER':
					s3dpIdx = nearestStrokeInPoints[i][0]
					stroke3dp = stroke3dPoints[s3dpIdx]
					if s3dpIdx+1 < len(stroke3dPoints):
						stroke3dd = stroke3dPoints[s3dpIdx+1]-stroke3dp
					else:
						stroke3dd = stroke3dp-stroke3dPoints[s3dpIdx-1]
					if nearestStrokeInPoints[i][1]<0:
						stroke3dd = -1*stroke3dd
					rotmat = Matrix.Rotation(math.pi*fac,4,stroke3dd)
					v.co = rotmat*(v.co-stroke3dp)+stroke3dp
					fac = None
			if fac is not None:
				if self.push_trg == 'IND_NORMLS':
					shiftDir = v.normal
				if self.push_trg == 'SCALE_CENTER':
					s3dpIdx = nearestStrokeInPoints[i][0]
					stroke3dp = stroke3dPoints[s3dpIdx]
					shiftDir = stroke3dp - v.co
				v.co = v.co + shiftDir*fac
		# Recalculate mesh normals (so lighting looks right).
		for edge in bm.edges:
			edge.normal_update()
		# Push bmesh changes back to the actual mesh datablock.
		bmesh.update_edit_mesh(active_mesh, True)
		return {'FINISHED'}
Esempio n. 48
0
def draw_network(network, edge_thickness=0.25, node_size=3, directed=True):
    """ Takes assembled network/molecule data and draws to blender """

    # Add some mesh primitives
    bpy.ops.object.select_all(action="DESELECT")
    bpy.ops.mesh.primitive_uv_sphere_add()
    sphere = bpy.context.object
    bpy.ops.mesh.primitive_cylinder_add()
    cylinder = bpy.context.object
    cylinder.active_material = bpy.data.materials["light_gray"]
    bpy.ops.mesh.primitive_cone_add()
    cone = bpy.context.object
    cone.active_material = bpy.data.materials["light_gray"]

    # Keep references to all nodes and edges
    shapes = []
    # Keep separate references to shapes to be smoothed
    shapes_to_smooth = []

    # Draw nodes
    for key, node in network["nodes"].items():

        # Coloring rule for nodes. Edit this to suit your needs!
        col = node.get("color", choice(list(colors.keys())))

        # Copy mesh primitive and edit to make node
        # (You can change the shape of drawn nodes here)
        node_sphere = sphere.copy()
        node_sphere.data = sphere.data.copy()
        node_sphere.location = node["location"]
        node_sphere.dimensions = [node_size] * 3
        node_sphere.active_material = bpy.data.materials[col]
        bpy.context.scene.objects.link(node_sphere)
        shapes.append(node_sphere)
        shapes_to_smooth.append(node_sphere)

    # Draw edges
    for edge in network["edges"]:

        # Get source and target locations by drilling down into data structure
        source_loc = network["nodes"][edge["source"]]["location"]
        target_loc = network["nodes"][edge["target"]]["location"]

        diff = [c2 - c1 for c2, c1 in zip(source_loc, target_loc)]
        cent = [(c2 + c1) / 2 for c2, c1 in zip(source_loc, target_loc)]
        mag = sum([(c2 - c1) ** 2 for c1, c2 in zip(source_loc, target_loc)]) ** 0.5

        # Euler rotation calculation
        v_axis = Vector(diff).normalized()
        v_obj = Vector((0, 0, 1))
        v_rot = v_obj.cross(v_axis)
        angle = acos(v_obj.dot(v_axis))

        # Copy mesh primitive to create edge
        edge_cylinder = cylinder.copy()
        edge_cylinder.data = cylinder.data.copy()
        edge_cylinder.dimensions = [edge_thickness] * 2 + [mag - node_size]
        edge_cylinder.location = cent
        edge_cylinder.rotation_mode = "AXIS_ANGLE"
        edge_cylinder.rotation_axis_angle = [angle] + list(v_rot)
        bpy.context.scene.objects.link(edge_cylinder)
        shapes.append(edge_cylinder)
        shapes_to_smooth.append(edge_cylinder)

        # Copy another mesh primitive to make an arrow head
        if directed:
            arrow_cone = cone.copy()
            arrow_cone.data = cone.data.copy()
            arrow_cone.dimensions = [edge_thickness * 4.0] * 3
            arrow_cone.location = cent
            arrow_cone.rotation_mode = "AXIS_ANGLE"
            arrow_cone.rotation_axis_angle = [angle + pi] + list(v_rot)
            bpy.context.scene.objects.link(arrow_cone)
            shapes.append(arrow_cone)

    # Remove primitive meshes
    bpy.ops.object.select_all(action="DESELECT")
    sphere.select = True
    cylinder.select = True
    cone.select = True

    # If the starting cube is there, remove it
    if "Cube" in bpy.data.objects.keys():
        bpy.data.objects.get("Cube").select = True
    bpy.ops.object.delete()

    # Smooth specified shapes
    for shape in shapes_to_smooth:
        shape.select = True
    bpy.context.scene.objects.active = shapes_to_smooth[0]
    bpy.ops.object.shade_smooth()

    # Join shapes
    for shape in shapes:
        shape.select = True
    bpy.context.scene.objects.active = shapes[0]
    bpy.ops.object.join()

    # Center object origin to geometry
    bpy.ops.object.origin_set(type="ORIGIN_GEOMETRY", center="MEDIAN")

    # Refresh scene
    bpy.context.scene.update()
Esempio n. 49
0
    def execute(self, context):
        object = context.object
        object.update_from_editmode()

        object_bm = bmesh.from_edit_mesh(object.data)
        object_bm.verts.ensure_lookup_table()
        object_bm.edges.ensure_lookup_table()
        object_bm.faces.ensure_lookup_table()

        selected_faces = [f for f in object_bm.faces if f.select]
        selected_edges = [e for e in object_bm.edges if e.select]
        selected_verts = [v for v in object_bm.verts if v.select]

        selection_center = Vector()

        if len(selected_edges) == 0:
            self.report({'WARNING'}, "Please select edges.")
            return {'CANCELLED'}

        try:
            cache_loops = get_cache(self.as_pointer(), "loops")
            loops = []
            for (loop_verts, loop_edges, loop_faces), is_loop_cyclic, is_loop_boundary in cache_loops:
                loops.append((([object_bm.verts[v] for v in loop_verts], [object_bm.edges[e] for e in loop_edges],
                               [object_bm.faces[f] for f in loop_faces]), is_loop_cyclic, is_loop_boundary))
        except CacheException:
            loops = get_loops(selected_edges, selected_faces)
            if loops:
                cache_loops = []
                for (loop_verts, loop_edges, loop_faces), is_loop_cyclic, is_loop_boundary in loops:
                    cache_loops.append((([v.index for v in loop_verts], [e.index for e in loop_edges],
                                         [f.index for f in loop_faces]), is_loop_cyclic, is_loop_boundary))
                set_cache(self.as_pointer(), "loops", cache_loops)

        if loops is None:
            self.report({'WARNING'}, "Please select boundary loop(s) of selected area(s).")
            return {'CANCELLED'}

        for vert in selected_verts:
            selection_center += vert.co
        selection_center /= len(selected_verts)

        object_bvh = mathutils.bvhtree.BVHTree.FromObject(object, context.scene, deform=False)

        refresh_icons()
        shape_bm = bmesh.new()
        for loop_idx, ((loop_verts, loop_edges, loop_faces), is_loop_cyclic, is_loop_boundary) in enumerate(loops):
            if len(loop_edges) < 3:
                continue
            loop_verts_len = len(loop_verts)

            shape_verts = None
            shape_edges = None
            try:
                cache_verts = get_cache(self.as_pointer(), "shape_verts_{}".format(loop_idx))
                tmp_vert = None
                for i, cache_vert in enumerate(cache_verts):
                    new_vert = shape_bm.verts.new(cache_vert)
                    if i > 0:
                        shape_bm.edges.new((tmp_vert, new_vert))
                    tmp_vert = new_vert
                shape_verts = shape_bm.verts[:]
                shape_bm.edges.new((shape_verts[-1], shape_verts[0]))
                shape_edges = shape_bm.edges[:]
            except CacheException:
                if self.shape == "CIRCLE":
                    a = sum([e.calc_length() for e in loop_edges]) / loop_verts_len
                    diameter = a / (2 * math.sin(math.pi / loop_verts_len))
                    shape_segments = loop_verts_len + self.span
                    shape_verts = bmesh.ops.create_circle(shape_bm, segments=shape_segments, diameter=diameter)
                    shape_verts = shape_verts["verts"]
                    shape_edges = shape_bm.edges[:]
                elif self.shape == "RECTANGLE":
                    if loop_verts_len % 2 > 0:
                        self.report({'WARNING'}, "An odd number of edges.")
                        del shape_bm
                        return {'FINISHED'}
                    size = sum([e.calc_length() for e in loop_edges])

                    size_a = (size / 2) / (self.ratio_a + self.ratio_b) * self.ratio_a
                    size_b = (size / 2) / (self.ratio_a + self.ratio_b) * self.ratio_b
                    seg_a = (loop_verts_len / 2) / (self.ratio_a + self.ratio_b) * self.ratio_a
                    seg_b = int((loop_verts_len / 2) / (self.ratio_a + self.ratio_b) * self.ratio_b)
                    if seg_a % 1 > 0:
                        self.report({'WARNING'}, "Incorrect sides ratio.")
                        seg_a += 1
                        seg_b += 2
                    seg_a = int(seg_a)
                    if self.is_square:
                        size_a = (size_a + size_b) / 2
                        size_b = size_a
                    seg_len_a = size_a / seg_a
                    seg_len_b = size_b / seg_b

                    for i in range(seg_a):
                        shape_bm.verts.new(Vector((size_b / 2 * -1, seg_len_a * i - (size_a / 2), 0)))
                    for i in range(seg_b):
                        shape_bm.verts.new(Vector((seg_len_b * i - (size_b / 2), size_a / 2, 0)))
                    for i in range(seg_a, 0, -1):
                        shape_bm.verts.new(Vector((size_b / 2, seg_len_a * i - (size_a / 2), 0)))
                    for i in range(seg_b, 0, -1):
                        shape_bm.verts.new(Vector((seg_len_b * i - (size_b / 2), size_a / 2 * -1, 0)))

                    shape_verts = shape_bm.verts[:]
                    for i in range(len(shape_verts)):
                        shape_bm.edges.new((shape_verts[i], shape_verts[(i + 1) % len(shape_verts)]))
                    shape_edges = shape_bm.edges[:]
                elif self.shape == "PATTERN":
                    pattern_idx = context.scene.perfect_shape.active_pattern
                    pattern = context.scene.perfect_shape.patterns[int(pattern_idx)]
                    if len(pattern.verts) == 0:
                        self.report({'WARNING'}, "Empty Pattern Data.")
                        del shape_bm
                        return {'FINISHED'}
                    if len(pattern.verts) != len(loop_verts):
                        self.report({'WARNING'}, "Pattern and loop vertices count must be the same.")
                        del shape_bm
                        return {'FINISHED'}
                    for pattern_vert in pattern.verts:
                        shape_bm.verts.new(Vector(pattern_vert.co))
                    shape_verts = shape_bm.verts[:]
                    for i in range(len(shape_verts)):
                        shape_bm.edges.new((shape_verts[i], shape_verts[(i + 1) % len(shape_verts)]))
                    shape_edges = shape_bm.edges[:]
                elif self.shape == "OBJECT":
                    if self.target in bpy.data.objects:
                        shape_object = bpy.data.objects[self.target]
                        shape_bm.from_object(shape_object, context.scene)
                        loops = get_loops(shape_bm.edges[:])
                        if not loops or len(loops) > 1:
                            self.report({'WARNING'}, "Wrong mesh data.")
                            del shape_bm
                            return {'FINISHED'}
                        if len(loops[0][0][0]) > len(loop_verts):
                            self.report({'WARNING'}, "Shape and loop vertices count must be the same.")
                            del shape_bm
                            return {'FINISHED'}

                        shape_verts = loops[0][0][0]
                        shape_edges = loops[0][0][1]
                if shape_verts:
                    set_cache(self.as_pointer(), "shape_verts_{}".format(loop_idx), [v.co.copy() for v in shape_verts])

            if shape_verts is not None and len(shape_verts) > 0:
                if context.space_data.pivot_point == "CURSOR":
                    center = object.matrix_world.copy() * context.scene.cursor_location.copy()
                else:
                    temp_bm = bmesh.new()
                    for loop_vert in loop_verts:
                        temp_bm.verts.new(loop_vert.co.copy())
                    temp_verts = temp_bm.verts[:]
                    for i in range(len(temp_verts)):
                        temp_bm.edges.new((temp_verts[i], temp_verts[(i + 1) % len(temp_verts)]))
                    temp_bm.faces.new(temp_bm.verts)
                    temp_bm.faces.ensure_lookup_table()
                    if context.space_data.pivot_point == 'BOUNDING_BOX_CENTER':
                        center = temp_bm.faces[0].calc_center_bounds()
                    else:
                        center = temp_bm.faces[0].calc_center_median()
                    del temp_bm

                context.scene.perfect_shape.preview_verts_count = loop_verts_len + self.span
                if self.projection == "NORMAL":
                    forward = calculate_normal([v.co.copy() for v in loop_verts])
                    normal_forward = reduce(
                        lambda v1, v2: v1.normal.copy() + v2.normal.copy() if isinstance(v1, bmesh.types.BMVert)
                        else v1.copy() + v2.normal.copy(), loop_verts).normalized()

                    if normal_forward.angle(forward) - math.pi / 2 > 1e-6:
                        forward.negate()
                else:
                    forward = Vector([v == self.projection for v in ["X", "Y", "Z"]])

                if self.invert_projection:
                    forward.negate()

                matrix_rotation = forward.to_track_quat('Z', 'Y').to_matrix().to_4x4()
                matrix_translation = Matrix.Translation(center)

                bmesh.ops.scale(shape_bm, vec=Vector((1, 1, 1)) * (1 + self.offset), verts=shape_verts)

                bmesh.ops.transform(shape_bm, verts=shape_verts, matrix=matrix_translation * matrix_rotation)

                if not is_clockwise(forward, center, loop_verts):
                    loop_verts.reverse()
                    loop_edges.reverse()

                if not is_clockwise(forward, center, shape_verts):
                    shape_verts.reverse()

                correct_angle_m = 1
                shift_m = 1

                if context.space_data.pivot_point != "INDIVIDUAL_ORIGINS":
                    if selection_center.dot(center.cross(forward)) >= 0:
                        # if    (center.cross(forward)).angle(selection_center) - math.pi/2 <= 1e-6:
                        correct_angle_m = -1
                        shift_m = -1

                loop_verts_co_2d, shape_verts_co_2d = None, None
                if loop_verts_co_2d is None:
                    loop_verts_co_2d = [(matrix_rotation.transposed() * v.co).to_2d() for v in loop_verts]
                    shape_verts_co_2d = [(matrix_rotation.transposed() * v.co).to_2d() for v in shape_verts]
                loop_angle = box_fit_2d(loop_verts_co_2d)
                shape_angle = box_fit_2d(shape_verts_co_2d)
                # if round(loop_angle, 4) == round(math.pi, 4):
                #     loop_angle = 0

                correct_angle = 0
                if self.loop_rotation:
                    correct_angle = loop_angle * correct_angle_m
                    if round(loop_angle, 3) == round(shape_angle, 3):
                        correct_angle -= shape_angle

                if self.shape_rotation:
                    correct_angle -= shape_angle

                if correct_angle != 0:
                    bmesh.ops.rotate(shape_bm, verts=shape_verts, cent=center,
                                     matrix=Matrix.Rotation(-correct_angle * correct_angle_m, 3, forward))

                kd_tree = mathutils.kdtree.KDTree(len(loop_verts))
                for idx, loop_vert in enumerate(loop_verts):
                    kd_tree.insert(loop_vert.co, idx)
                kd_tree.balance()
                shape_first_idx = kd_tree.find(shape_verts[0].co)[1]
                shift = shape_first_idx + self.shift * shift_m
                if shift != 0:
                    loop_verts = loop_verts[shift % len(loop_verts):] + loop_verts[:shift % len(loop_verts)]

                if self.rotation != 0:
                    bmesh.ops.rotate(shape_bm, verts=shape_verts, cent=center,
                                     matrix=Matrix.Rotation(-self.rotation * correct_angle_m, 3, forward))

                bmesh.ops.translate(shape_bm, vec=self.shape_translation, verts=shape_bm.verts)
                center = Matrix.Translation(self.shape_translation) * center

                if not is_loop_boundary and self.use_ray_cast:
                    for shape_vert in shape_verts:
                        co = shape_vert.co
                        ray_cast_data = object_bvh.ray_cast(co, forward)
                        if ray_cast_data[0] is None:
                            ray_cast_data = object_bvh.ray_cast(co, -forward)
                        if ray_cast_data[0] is not None:
                            shape_vert.co = ray_cast_data[0]

                for idx, vert in enumerate(loop_verts):
                    vert.co = vert.co.lerp(shape_verts[idx].co, self.factor / 100)

                if not is_loop_boundary and is_loop_cyclic and loop_faces:
                    if self.fill_type != "ORIGINAL":
                        smooth = loop_faces[0].smooth
                        bmesh.ops.delete(object_bm, geom=loop_faces, context=5)

                        loop_faces = []
                        center_vert = object_bm.verts.new(center)
                        if self.use_ray_cast:
                            ray_cast_data = object_bvh.ray_cast(center_vert.co, forward)
                            if ray_cast_data[0] is None:
                                ray_cast_data = object_bvh.ray_cast(center_vert.co, -forward)
                            if ray_cast_data[0] is not None:
                                center_vert.co = ray_cast_data[0]
                        for idx, vert in enumerate(loop_verts):
                            new_face = object_bm.faces.new((center_vert, vert, loop_verts[(idx + 1) % loop_verts_len]))
                            new_face.smooth = smooth
                            loop_faces.append(new_face)
                        bmesh.ops.recalc_face_normals(object_bm, faces=loop_faces)

                    if self.outset > 0.0:
                        outset_region_faces = bmesh.ops.inset_region(object_bm, faces=loop_faces,
                                                                     thickness=self.outset, use_even_offset=True,
                                                                     use_interpolate=True, use_outset=True)

                    if self.extrude == 0:
                        verts = loop_verts[:]
                        for face in loop_faces:
                            for vert in face.verts:
                                if vert not in verts:
                                    verts.append(vert)
                        if self.fill_flatten:
                            matrix = Matrix.Translation(-center)
                            bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation.transposed(),
                                             verts=loop_verts)
                            bmesh.ops.scale(object_bm, vec=Vector((1, 1, +0)), space=matrix, verts=verts)
                            bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation, verts=verts)

                        if self.inset > 0.0:
                            bmesh.ops.inset_region(object_bm, faces=loop_faces,
                                                   thickness=self.inset,
                                                   use_even_offset=True,
                                                   use_interpolate=True)
                        if self.fill_type == "HOLE":
                            bmesh.ops.delete(object_bm, geom=loop_faces, context=5)
                        elif self.fill_type == "NGON":
                            bmesh.utils.face_join(loop_faces)

                    else:
                        verts = []
                        edges = []
                        faces = []
                        side_faces = []
                        side_edges = []
                        extrude_geom = bmesh.ops.extrude_face_region(object_bm, geom=loop_faces, use_keep_orig=True)
                        bmesh.ops.delete(object_bm, geom=loop_faces, context=5)
                        for geom in extrude_geom["geom"]:
                            if isinstance(geom, bmesh.types.BMVert):
                                verts.append(geom)
                            elif isinstance(geom, bmesh.types.BMFace):
                                faces.append(geom)
                            elif isinstance(geom, bmesh.types.BMEdge):
                                edges.append(geom)

                        for edge in loop_edges:
                            for face in edge.link_faces:
                                if any((e for e in face.edges if e in edges)):
                                    side_faces.append(face)
                                    for edge in face.edges:
                                        if edge not in side_edges and edge not in edges and edge not in loop_edges:
                                            side_edges.append(edge)

                        if self.fill_flatten:
                            matrix = Matrix.Translation(-center)
                            bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation.transposed(), verts=verts)
                            bmesh.ops.scale(object_bm, vec=Vector((1.0, 1.0, 0.001)), space=matrix, verts=verts)
                            bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation, verts=verts)

                        bmesh.ops.translate(object_bm,
                                            verts=verts,
                                            vec=forward * self.extrude)

                        cuts = max(self.cuts, self.cuts_rings)
                        if cuts > 0:
                            sub = bmesh.ops.subdivide_edges(object_bm, edges=side_edges, cuts=cuts)
                            loop_verts = []
                            first_verts = loop_edges[0].verts[:]
                            for edge in loop_edges:
                                if edge == loop_edges[0]:
                                    continue
                                if edge == loop_edges[1]:
                                    if first_verts[0] == edge.verts[0]:
                                        first_verts.reverse()
                                    loop_verts.extend(first_verts)
                                for vert in edge.verts:
                                    if vert not in loop_verts:
                                        loop_verts.append(vert)
                            split_edges = []
                            for geom in sub["geom_split"]:
                                if isinstance(geom, bmesh.types.BMEdge):
                                    split_edges.append(geom)

                            skip_edges = []
                            for vert in loop_verts:
                                for edge in vert.link_edges:
                                    if edge not in skip_edges and edge not in split_edges:
                                        skip_edges.append(edge)

                            start = self.cuts_shift % loop_verts_len
                            stop = self.cuts_shift % loop_verts_len
                            verts_list = loop_verts[start:] + loop_verts[:stop]
                            for i in range(self.cuts):
                                new_split_edges = []
                                for idx, vert in enumerate(verts_list):
                                    if idx < self.cuts_len + i or idx >= loop_verts_len - i:
                                        continue
                                    for edge in vert.link_edges:
                                        if edge in split_edges:
                                            other_vert = edge.other_vert(vert)
                                            bmesh.ops.weld_verts(object_bm, targetmap={other_vert: vert})
                                    for edge in vert.link_edges:
                                        if edge not in new_split_edges and edge not in skip_edges:
                                            new_split_edges.append(edge)

                                split_edges = new_split_edges

                            cut_edges = []
                            dissolve_edges = []
                            cut_skip = []
                            prev_edge = None
                            first_join = True
                            for i in range(self.cuts_rings):
                                if i >= self.cuts:
                                    break
                                split_edges = []
                                for idx, vert in enumerate(verts_list):
                                    if idx < self.cuts_len + i or idx >= loop_verts_len - i:
                                        for edge in vert.link_edges:
                                            if edge not in skip_edges and edge not in cut_skip:
                                                if prev_edge is not None and idx not in range(self.cuts_len):
                                                    if not any((v for v in edge.verts if v in prev_edge.verts)):
                                                        if edge not in dissolve_edges:
                                                            dissolve_edges.append(edge)

                                                if edge not in dissolve_edges and edge not in split_edges:
                                                    split_edges.append(edge)
                                                    cut_skip.append(edge)
                                                    # edge.select_set(True)
                                                prev_edge = edge

                                if first_join and len(cut_edges) == 1:
                                    cut_edges[0].extend(split_edges)
                                    first_join = False
                                else:
                                    cut_edges.append(split_edges)
                            # if dissolve_edges:
                            #     bmesh.ops.dissolve_edges(object_bm, edges=dissolve_edges)
                            # inner_verts = []
                            # for i, split_edges in enumerate(cut_edges):
                            #     for edge in split_edges:
                            #         sub = bmesh.ops.subdivide_edges(object_bm, edges=[edge], cuts=self.cuts-i)
                            #         sub_verts = [v for v in sub["geom_inner"] if isinstance(v, bmesh.types.BMVert)]
                            #         inner_verts.append(sub_verts)

                        if self.side_inset > 0.0:
                            inset_region = bmesh.ops.inset_region(object_bm, faces=side_faces,
                                                                  thickness=self.side_inset,
                                                                  use_even_offset=True, use_interpolate=True)

                        if self.inset > 0.0:
                            inset_region_faces = bmesh.ops.inset_region(object_bm, faces=faces, thickness=self.inset,
                                                                        use_even_offset=True, use_interpolate=True)
                        if self.fill_type == "HOLE":
                            bmesh.ops.delete(object_bm, geom=faces, context=5)
                        elif self.fill_type == "NGON":
                            bmesh.utils.face_join(faces)

            shape_bm.clear()

        del object_bvh
        object_bm.normal_update()
        bmesh.update_edit_mesh(object.data)
        return {'FINISHED'}
Esempio n. 50
0
	def iterate(self, newendpointsper1000=0, maxtime=0.0): # maxtime still ignored for now
		
		endpointsadded=0.0
		niterations=0.0
		newendpointsper1000 /= 1000.0
		t=expovariate(newendpointsper1000) if newendpointsper1000 > 0.0 else 1 # time to the first new 'endpoint add event'
		
		while self.NBP>0 and (len(self.endpoints)>0):
			self.NBP -= 1
			closestsendpoints=dd(list)

			kill = set()
			
			for ei,e in enumerate(self.endpoints):
				distance = None
				closestbp = None
				for bi,b in enumerate(self.branchpoints):
					ddd = b.v-e
					ddd = ddd.dot(ddd)
					if ddd < self.KILLDIST:
						kill.add(ei)
					elif (ddd<self.INFLUENCE and b.shoot is None) and ((distance is None) or (ddd < distance)):
						closestbp = bi
						distance = ddd
				if not (closestbp is None):
					closestsendpoints[closestbp].append(ei)
			
			if len(closestsendpoints)<1:
				break
			
			for bi in closestsendpoints:
				sd=Vector((0,0,0))
				n=0
				for ei in closestsendpoints[bi]:
					dv=self.branchpoints[bi].v-self.endpoints[ei]
					ll=sqrt(dv.dot(dv))
					sd-=dv/ll
					n+=1
				sd/=n
				ll=sqrt(sd.dot(sd))
				# don't know if this assumption is true:
				# if the unnormalised direction is very small, the endpoints are nearly coplanar/colinear and at roughly the same distance
				# so no endpoints will be killed and we might end up adding the same branch again and again
				if ll < 1e-3 :
					#print('SD very small')
					continue
					
				sd/=ll
				sd[2]+=self.TROPISM
				ll=sqrt(sd.dot(sd))
				sd/=ll
			
				newp = self.branchpoints[bi].v+sd*self.d
				# the assumption we made earlier is not suffucient to prevent adding the same branch so we need an expensive check:
				tooclose = False
				for dbi in self.branchpoints:
					dddd = newp - dbi.v
					if dddd.dot(dddd) < 1e-3 :
						#print('BP to close to another')
						tooclose = True
				if tooclose : continue
				
				if not self.exclude(newp):
					bp = Branchpoint(newp,bi)
					self.branchpoints.append(bp)
					nbpi = len(self.branchpoints)-1
					bp = self.branchpoints[bi]
					bp.connections+=1
					if bp.apex is None:
						bp.apex = nbpi
					else:
						bp.shoot = nbpi
					while not (bp.parent is None):
						bp = self.branchpoints[bp.parent]
						bp.connections+=1
					
			self.endpoints = [ep for ei,ep in enumerate(self.endpoints) if not(ei in kill)]
		
			if newendpointsper1000 > 0.0:
				# generate new endpoints with a poisson process
				# when we first arrive here, t already hold the time to the first event
				niterations+=1
				while t < niterations: # we keep on adding endpoints as long as the next event still happens within this iteration
					self.endpoints.append(next(self.volumepoint))
					endpointsadded+=1
					t+=expovariate(newendpointsper1000) # time to new 'endpoint add event'
Esempio n. 51
0
def draw_molecule(molecule, center=(0, 0, 0), show_bonds=True, join=True):
    """Draws a JSON-formatted molecule in Blender.

    This method uses a couple of tricks from [1] to improve rendering speed.
    In particular, it minimizes the amount of unique meshes and materials,
    and doesn't draw until all objects are initialized.

    [1] https://blenderartists.org/forum/showthread.php
        ?273149-Generating-a-large-number-of-mesh-primitives

    Args:
        molecule: The molecule to be drawn, as a python object following the
            JSON convention set in this project.
        center: (Optional, default (0, 0, 0)) Cartesian center of molecule. Use
            to draw multiple molecules in different locations.
        show_bonds: (Optional, default True) Draws a ball-and-stick model if
            True, and a space-filling model if False.
        join: (Optional, default True) Joins the molecule into a single object.
            Set to False if you want to individually manipulate atoms/bonds.
    Returns:
        If run in a blender context, will return a visual object of the
        molecule.
    """
    shapes = []

    # If using space-filling model, scale up atom size and remove bonds

    # Add atom primitive
    bpy.ops.object.select_all(action='DESELECT')
    bpy.ops.mesh.primitive_uv_sphere_add()
    sphere = bpy.context.object

    # Initialize bond material if it's going to be used.
    if show_bonds:
        bpy.data.materials.new(name='bond')
        bpy.data.materials['bond'].diffuse_color = atom_data['bond']['color']
        bpy.data.materials['bond'].specular_intensity = 0.2
        bpy.ops.mesh.primitive_cylinder_add()
        cylinder = bpy.context.object
        cylinder.active_material = bpy.data.materials['bond']

    for atom in molecule['atoms']:
        if atom['element'] not in atom_data:
            atom['element'] = 'undefined'

        if atom['element'] not in bpy.data.materials:
            key = atom['element']
            bpy.data.materials.new(name=key)
            bpy.data.materials[key].diffuse_color = atom_data[key]['color']
            bpy.data.materials[key].specular_intensity = 0.2

        atom_sphere = sphere.copy()
        atom_sphere.data = sphere.data.copy()
        atom_sphere.location = [l + c for l, c in
                                zip(atom['location'], center)]
        scale = 1 if show_bonds else 2.5
        atom_sphere.dimensions = [atom_data[atom['element']]['radius'] *
                                  scale * 2] * 3
        atom_sphere.active_material = bpy.data.materials[atom['element']]
        bpy.context.scene.objects.link(atom_sphere)
        shapes.append(atom_sphere)

    for bond in (molecule['bonds'] if show_bonds else []):
        start = molecule['atoms'][bond['atoms'][0]]['location']
        end = molecule['atoms'][bond['atoms'][1]]['location']
        diff = [c2 - c1 for c2, c1 in zip(start, end)]
        cent = [(c2 + c1) / 2 for c2, c1 in zip(start, end)]
        mag = sum([(c2 - c1) ** 2 for c1, c2 in zip(start, end)]) ** 0.5

        v_axis = Vector(diff).normalized()
        v_obj = Vector((0, 0, 1))
        v_rot = v_obj.cross(v_axis)

        # This check prevents gimbal lock (ie. weird behavior when v_axis is
        # close to (0, 0, 1))
        if v_rot.length > 0.01:
            v_rot = v_rot.normalized()
            axis_angle = [acos(v_obj.dot(v_axis))] + list(v_rot)
        else:
            v_rot = Vector((1, 0, 0))
            axis_angle = [0] * 4

        if bond['order'] not in range(1, 4):
            sys.stderr.write("Improper number of bonds! Defaulting to 1.\n")
            bond['order'] = 1

        if bond['order'] == 1:
            trans = [[0] * 3]
        elif bond['order'] == 2:
            trans = [[1.4 * atom_data['bond']['radius'] * x for x in v_rot],
                     [-1.4 * atom_data['bond']['radius'] * x for x in v_rot]]
        elif bond['order'] == 3:
            trans = [[0] * 3,
                     [2.2 * atom_data['bond']['radius'] * x for x in v_rot],
                     [-2.2 * atom_data['bond']['radius'] * x for x in v_rot]]

        for i in range(bond['order']):
            bond_cylinder = cylinder.copy()
            bond_cylinder.data = cylinder.data.copy()
            bond_cylinder.dimensions = [atom_data['bond']['radius'] * scale *
                                        2] * 2 + [mag]
            bond_cylinder.location = [c + scale * v for c,
                                      v in zip(cent, trans[i])]
            bond_cylinder.rotation_mode = 'AXIS_ANGLE'
            bond_cylinder.rotation_axis_angle = axis_angle
            bpy.context.scene.objects.link(bond_cylinder)
            shapes.append(bond_cylinder)

    # Remove primitive meshes
    bpy.ops.object.select_all(action='DESELECT')
    sphere.select = True
    if show_bonds:
        cylinder.select = True
    # If the starting cube is there, remove it
    if 'Cube' in bpy.data.objects.keys():
        bpy.data.objects.get('Cube').select = True
    bpy.ops.object.delete()

    for shape in shapes:
        shape.select = True
    bpy.context.scene.objects.active = shapes[0]
    bpy.ops.object.shade_smooth()
    if join:
        bpy.ops.object.join()

    bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='MEDIAN')
    bpy.context.scene.update()
def draw_molecule(molecule, center=(0, 0, 0), max_molecule_size=5,
                  show_bonds=True):
    """Draw a molecule to blender. Uses loaded json molecule data."""

    # Get scale factor - only scales large molecules down
    max_coord = 1E-6
    for atom in molecule["atoms"]:
        max_coord = max(max_coord, *[abs(a) for a in atom["location"]])
    scale = min(max_molecule_size / max_coord, 1)

    # Scale location coordinates and add specified center
    for atom in molecule["atoms"]:
        atom["location"] = [c + x * scale for c, x in zip(center,
                                                          atom["location"])]

    # Keep references to all atoms and bonds
    shapes = []

    # If using space-filling model, scale up atom size and remove bonds
    if not show_bonds:
        scale *= 2.5
        molecule["bonds"] = []

    # Add atom primitive
    bpy.ops.object.select_all(action='DESELECT')
    bpy.ops.mesh.primitive_uv_sphere_add()
    sphere = bpy.context.object

    # Add bond material and primitive if it's going to be used
    if molecule["bonds"]:
        key = "bond"
        bpy.data.materials.new(name=key)
        bpy.data.materials[key].diffuse_color = colors[key]
        bpy.data.materials[key].specular_intensity = 0.2
        bpy.ops.mesh.primitive_cylinder_add()
        cylinder = bpy.context.object
        cylinder.active_material = bpy.data.materials["bond"]

    # Draw atoms
    for atom in molecule["atoms"]:

        # If element is not in dictionary, use undefined values
        if atom["element"] not in available_atoms:
            atom["element"] = "undefined"

        # If material for atom type has not yet been defined, do so
        if atom["element"] not in bpy.data.materials:
            key = atom["element"]
            bpy.data.materials.new(name=key)
            bpy.data.materials[key].diffuse_color = colors[key]
            bpy.data.materials[key].specular_intensity = 0.2

        # Copy mesh primitive and edit to make atom
        atom_sphere = sphere.copy()
        atom_sphere.data = sphere.data.copy()
        atom_sphere.location = atom["location"]
        atom_sphere.dimensions = [diameters[atom["element"]] * scale] * 3
        atom_sphere.active_material = bpy.data.materials[atom["element"]]
        bpy.context.scene.objects.link(atom_sphere)
        shapes.append(atom_sphere)

    # Draw bonds
    for bond in molecule["bonds"]:

        # Extracting locations
        first_loc = molecule["atoms"][bond["atoms"][0]]["location"]
        second_loc = molecule["atoms"][bond["atoms"][1]]["location"]
        diff = [c2 - c1 for c2, c1 in zip(first_loc, second_loc)]
        cent = [(c2 + c1) / 2 for c2, c1 in zip(first_loc, second_loc)]
        mag = sum([(c2 - c1) ** 2
                   for c1, c2 in zip(first_loc, second_loc)]) ** 0.5

        # Euler rotation calculation
        v_axis = Vector(diff).normalized()
        v_obj = Vector((0, 0, 1))
        v_rot = v_obj.cross(v_axis)
        angle = acos(v_obj.dot(v_axis))

        # Check that the number of bonds is logical
        if bond["order"] not in range(1, 4):
            print("Improper number of bonds! Defaulting to 1.")
            bond["order"] = 1

        # Specify locations of each bond in every scenario
        if bond["order"] == 1:
            trans = [[0] * 3]
        elif bond["order"] == 2:
            trans = [[0.7 * diameters["bond"] * x for x in v_obj],
                    [-0.7 * diameters["bond"] * x for x in v_obj]]
        elif bond["order"] == 3:
            trans = [[0] * 3, [1.1 * diameters["bond"] * x for x in v_obj],
                    [-1.1 * diameters["bond"] * x for x in v_obj]]
        # Draw bonds
        for i in range(bond["order"]):
            bond_cylinder = cylinder.copy()
            bond_cylinder.data = cylinder.data.copy()
            bond_cylinder.dimensions = [diameters["bond"] * scale] * 2 + [mag]
            bond_cylinder.location = [c + scale * v for c,
                                      v in zip(cent, trans[i])]
            bond_cylinder.rotation_mode = "AXIS_ANGLE"
            bond_cylinder.rotation_axis_angle = [angle] + list(v_rot)
            bpy.context.scene.objects.link(bond_cylinder)
            shapes.append(bond_cylinder)

    # Remove primitive meshes
    bpy.ops.object.select_all(action='DESELECT')
    sphere.select = True
    if molecule["bonds"]:
        cylinder.select = True
    # If the starting cube is there, remove it
    if "Cube" in bpy.data.objects.keys():
        bpy.data.objects.get("Cube").select = True
    bpy.ops.object.delete()

    # Smooth and join molecule shapes
    for shape in shapes:
        shape.select = True
    bpy.context.scene.objects.active = shapes[0]
    bpy.ops.object.shade_smooth()
    bpy.ops.object.join()

    # Center object origin to geometry
    bpy.ops.object.origin_set(type="ORIGIN_GEOMETRY", center="MEDIAN")

    # Refresh scene
    bpy.context.scene.update()
Esempio n. 53
0
class PlaneVector(Vector):
    """
    三次元平面を表す四次元ベクトル。
    
    attributes:
        location (Vector):
            平面の位置ベクトル。
        normal (Vector):
            平面の法線ベクトル。
    
    tip:
        参考: http://marupeke296.com/COL_Basic_No3_WhatIsPlaneEquation.html
        法線ベクトル= [a, b, c]  # need normalize
        位置ベクトル = [xo, yo, zo]
        
        平面の方程式。
        a(x -  xo) + b(y - yo) + c(z - zo) = 0
        これは[a, b, c]と[(x - xo),(y - yo),(z - zo)]の内積と見ることも出来る。
        
        上式を展開
        ax + by + cz - (a * xo + b * yo + c * zo) = 0
        
        平面を表す四次元ベクトル pvec = [a, b, c, - (a * xo + b * yo + c * zo)]
        平面とベクトルの距離 = dot_v4v4(vec, pvec)
    
    caution:
        normalの正規化は、__new__(), update(), _normal_set()でしか行われない。
        スライスを使った変更(normal[:] = [1.0, 0.0, 0.0])をやると正規化が行われない。
        
        copy()以外の、normalized()やto_3d()等は只のVector型になってしまうので注意
    """

    def __new__(cls, location=Vector(), normal=ZAXIS, rotation=Quaternion()):
        loc = Vector(location)
        nor = Vector(normal).normalized()
        vector = nor.to_4d()
        vector[3] = -nor.dot(loc)
        return Vector.__new__(cls, vector)

    def __init__(self, location=Vector(), normal=ZAXIS, rotation=None):
        """
        location: <Vector>
        normal: <Vector>
        rotation: <Quaternion> or <None>
        """
        self._location = Vector(location)
        self._normal = Vector(normal).normalized()
        if rotation:
            self._rotation = Quaternion(rotation)
        else:
            self._rotation = ZAXIS.rotation_difference(self._normal)

    @property
    def normal_isnan(self):
        return any((math.isnan(f) for f in self.normal))

    @property
    def location_isnan(self):
        return any((math.isnan(f) for f in self.location))

    def copy(self, other=None):
        return self.__class__(self.location, self.normal, self.rotation)

    def copy_to(self, other):
        """other: <PlaneVector>: 対象へ値を複製する"""
        other[:] = self[:]
        other._location[:] = self._location
        other._normal[:] = self._normal
        other._rotation[:] = self._rotation

    def update(self):
        """self.location,self.normal,self.rotationを変更した際に呼び出される。
        x, y, z = self._location
        a, b, c = self._normal
        self[:] = [a, b, c, -(a * x + b * y + c * z)]
        """
        self._normal.normalize()
        self[:3] = self._normal
        self[3] = -self._normal.dot(self._location)

    @property
    def location(self):
        return self._location
    @location.setter
    def location(self, value):
        """スライスで代入"""
        # self._location = Vector(value)
        self._location[:] = value
        self.update()

    @property
    def normal(self):
        return self._normal
    @normal.setter
    def normal(self, value):
        """スライスで代入"""
        # self._normal = Vector(value).normalized()
        self._normal[:] = value
        self.update()
        self._rotation = ZAXIS.rotation_difference(self._normal)

    @property
    def rotation(self):
        return self._rotation
    @rotation.setter
    def rotation(self, value):
        self._rotation[:] = value
        self._normal[:] = self._rotation * ZAXIS
        self.update()

    def distance(self, other:Vector):
        """平面とVectorの距離を返す"""
        return self.dot(other.to_4d())

    def distance_normal(self, other:Vector):
        """normalの直線とVectorの距離を返す"""
        self.update()
        return self.normal.cross(other - self.location)

    def project(self, other:Vector):
        """上書き。Vectorを平面に投影する"""
        self.update()
        v = other - self.location
        return (v - v.project(self.normal)) + self.location

    def intersect(self, v1, v2):
        """平面とv1-v2からなる直線の交点を求める"""
        return geom.intersect_line_plane(v1, v2, self.location, self.normal)

    def same_radius_vectors(self, radius, num):
        """平面上にあってlocationを中心にした同一距離のベクトルを返す。"""
        if num == 0:
            return []
        quat = ZAXIS.rotation_difference(self.normal)
        vectors = []
        a = math.pi / 2  # 90度の位置から始める
        for i in range(num):
            vec = Vector((radius * math.cos(a), radius * math.sin(a), 0))
            vectors.append(quat * vec + self.location)
            a -= math.pi * 2 / num  # 反時計回り
        return vectors

    def to_matrix(self, use_rotation=False):
        """plane.normalがZ軸になるような行列。
        PlaneVector.to_matrix().inverted() * Vectorでworld->plane座標への変換が
        出来る。
        use_rotation: <bool>: self.rotationを使う。
        """
        locmat = Matrix.Translation(self.location)
        if use_rotation:
            quat = self.rotation
        else:
            quat = ZAXIS.rotation_difference(self.normal)
        rotmat = quat.to_matrix().to_4x4()
        return locmat * rotmat
Esempio n. 54
0
	def iterate(self, newendpointsper1000=0, maxtime=0.0): # maxtime still ignored for now
		
		endpointsadded=0.0
		niterations=0.0
		newendpointsper1000 /= 1000.0
		t=expovariate(newendpointsper1000) if newendpointsper1000 > 0.0 else 1 # time to the first new 'endpoint add event'
		
		while self.NBP>0 and (len(self.endpoints)>0):
			self.NBP -= 1
			closestsendpoints=dd(list)

			kill = set()
			
			for ei,e in enumerate(self.endpoints):
				distance = None
				closestbp = None
				for bi,b in enumerate(self.branchpoints):
					ddd = b.v-e
					ddd = ddd.dot(ddd)
					if ddd < self.KILLDIST:
						kill.add(ei)
					elif (ddd<self.INFLUENCE) and ((distance is None) or (ddd < distance)):
						closestbp = bi
						distance = ddd
				if not (closestbp is None):
					closestsendpoints[closestbp].append(ei)
			
			if len(closestsendpoints)<1:
				break
			
			for bi in closestsendpoints:
				sd=Vector((0,0,0))
				n=0
				for ei in closestsendpoints[bi]:
					dv=self.branchpoints[bi].v-self.endpoints[ei]
					ll=sqrt(dv.dot(dv))
					sd-=dv/ll
					n+=1
				sd/=n
				ll=sqrt(sd.dot(sd))
				sd/=ll
				sd[2]+=self.TROPISM
				ll=sqrt(sd.dot(sd))
				sd/=ll
			
				if sd < 1e-7 : print('SD very small')
				bp = Branchpoint(self.branchpoints[bi].v+sd*self.d,bi)
				self.branchpoints.append(bp)
				bp = self.branchpoints[bi]
				bp.connections+=1
				while not (bp.parent is None):
					bp = self.branchpoints[bp.parent]
					bp.connections+=1
				
			self.endpoints = [ep for ei,ep in enumerate(self.endpoints) if not(ei in kill)]
		
			if newendpointsper1000 > 0.0:
				# generate new endpoints with a poisson process
				# when we first arrive here, t already hold the time to the first event
				niterations+=1
				while t < niterations: # we keep on adding endpoints as long as the next event still happens within this iteration
					self.endpoints.append(next(self.volumepoint))
					endpointsadded+=1
					t+=expovariate(newendpointsper1000) # time to new 'endpoint add event'
				
		if newendpointsper1000 > 0.0:
			print("newendpoints/iteration %.3f, actual %.3f in %5.f iterations"%(newendpointsper1000,endpointsadded/niterations,niterations))
Esempio n. 55
0
  def grow(self):

    from numpy import sum, all, ones

    self.itt += 1

    v_xyz,s_xyz = self.get_positions()
    dvv,dvs = self.get_distances(v_xyz,s_xyz)
    vs_map,sv_map = self.make_maps(dvv,dvs)

    nv,ns = dvs.shape

    nodes = self.nodes
    stp = self.stp
    killzone = self.killzone
    noise = self.noise
    spawn_count = self.spawn_count

    new_node_pos = []

    for i,jj in vs_map.items():

      if spawn_count[i]>2:
        continue

      mask = dvs[i,jj]>=killzone
      if all(mask):

        spawn_count[i] += 1

        v = Vector(sum(s_xyz[jj,:]-v_xyz[i,:],axis=0))
        v.normalize()

        p,pn = self.closest_point_on_geom(nodes[i])

        projected = pn.cross(v.cross(pn))
        projected.normalize()

        if v.dot(projected)<0.85:
          new = projected
        else:
          new = v

        new += random_unit_vector()*noise
        new.normalize()

        new = nodes[i] + new*stp
        new_node_pos.append(new)

        bpy.ops.mesh.primitive_ico_sphere_add(
          size=0.3,
          subdivisions=1,
          location=new)

        self.rotate(v)
        self.boolean_union(bpy.context.object)

    ## mask out dead source nodes
    mask = ones(ns,'bool')
    for j,ii in sv_map.items():

      if all(dvs[ii,j]<=killzone):
        mask[j] = False
        self.num_sources -= 1

        bpy.ops.mesh.primitive_ico_sphere_add(
          size=0.3,
          subdivisions=1,
          location=self.sources[j])
        self.boolean_union(bpy.context.object)

        print('deleted source:',j)
        self.dead_sources.append(self.sources[j])

    self.nodes.extend(new_node_pos)
    self.sources = self.sources[mask,:]
    return