Example #1
0
def nautical_euler_from_axes(forward, right):
    x = Vector(right)
    y = Vector(forward)
    
    world_x = Vector((1, 0, 0))
    world_z = Vector((0, 0, 1))
    
    if abs(y.z) > (1 - 1e-12): # sufficiently close to vertical
        roll = 0.0
        xdir = x.copy()
    else:
        xdir = y.cross(world_z)
        rollPos = angle_signed(-y, x, xdir, 0.0)
        rollNeg = angle_signed(-y, x, -xdir, 0.0)
        if abs(rollNeg) < abs(rollPos):
            roll = rollNeg
            xdir = -xdir
        else:
            roll = rollPos
    xdir = Vector((xdir.x, xdir.y, 0)).normalized()
    
    yaw = angle_signed(-world_z, xdir, world_x, 0.0)
    
    zdir = xdir.cross(y).normalized()
    pitch = angle_signed(-xdir, zdir, world_z, 0.0)
    
    return Euler((pitch, roll, yaw), 'YXZ')
Example #2
0
 def arbitrary_x_axis(extrusion_normal):
     world_y = Vector((0, 1, 0))
     world_z = Vector((0, 0, 1))
     if abs(extrusion_normal[0]) < 1 / 64 and abs(extrusion_normal[1]) < 1 / 64:
         a_x = world_y.cross(extrusion_normal)
     else:
         a_x = world_z.cross(extrusion_normal)
     a_x.normalize()
     return a_x, extrusion_normal.cross(a_x)
Example #3
0
 def _gimbal_xyz(self, context, am):
     rotobj = (context.active_pose_bone if context.mode == 'POSE' else None) or context.active_object
     
     if (not rotobj) or (rotobj.rotation_mode == 'QUATERNION'):
         if not am: am = self.calc_active_matrix(context)
         xyz = am.col[:3]
     elif rotobj.rotation_mode == 'AXIS_ANGLE':
         aa = rotobj.rotation_axis_angle
         z = Vector(aa[1:]).normalized()
         q = Vector((0,0,1)).rotation_difference(z)
         x = (q * Vector((1,0,0))).normalized()
         y = z.cross(x)
     else:
         e = rotobj.rotation_euler
         m = Matrix.Identity(3)
         for e_ax in rotobj.rotation_mode:
             m = Matrix.Rotation(getattr(e, e_ax.lower()), 3, e_ax) * m
         x, y, z = m.col[:3]
     
     if not xyz:
         m = BlUtil.Object.matrix_parent(rotobj)
         x.rotate(m)
         y.rotate(m)
         z.rotate(m)
         xyz = x, y, z
     
     return xyz
  def rotate(self,v):
    up_axis = Vector((0.0, 0.0, 1.0))
    angle = v.angle(up_axis, 0)
    rotation_axis = up_axis.cross(v)
    bpy.ops.transform.rotate(value=angle,axis=rotation_axis)

    return
 def __get(self): # in object axes
     world_x = Vector((1, 0, 0))
     world_z = Vector((0, 0, 1))
     
     x = self.right # right
     y = self.forward # forward
     z = self.up # up
     
     if abs(y.z) > (1 - 1e-12): # sufficiently close to vertical
         roll = 0.0
         xdir = x.copy()
     else:
         xdir = y.cross(world_z)
         rollPos = angle_signed(-y, x, xdir, 0.0)
         rollNeg = angle_signed(-y, x, -xdir, 0.0)
         if abs(rollNeg) < abs(rollPos):
             roll = rollNeg
             xdir = -xdir
         else:
             roll = rollPos
     xdir = Vector((xdir.x, xdir.y, 0)).normalized()
     
     yaw = angle_signed(-world_z, xdir, world_x, 0.0)
     
     zdir = xdir.cross(y).normalized()
     pitch = angle_signed(-xdir, zdir, world_z, 0.0)
     
     return Euler((pitch, roll, yaw), 'YXZ')
def focus_view_on(region_3d, location):
    r3d = region_3d

    a = r3d.view_location.copy()
    b = location
    mm = r3d.view_matrix.inverted()

    vr = mm.to_3x3()
    loc = mm.translation

    n = (a-loc).cross(b-loc).normalized()
    alp = math.acos( max(-1.0,min(1.0,  (a-loc).normalized().dot( (b-loc).normalized() )  )))

    zero = Vector()
    u0,v0,w0 = vr.transposed()
    u = rot_on( zero, n, alp, u0 )
    v = rot_on( zero, n, alp, v0 )
    w = rot_on( zero, n, alp, w0 )

    if bpy.context.user_preferences.inputs.view_rotate_method == 'TURNTABLE':
        ez = Vector((0,0,1))
        u2 = ez.cross(w)
        v2 = w.cross(u2)
        u,v = u2,v2

    vr2 = Matrix((u,v,w)).transposed()

    mm2 = vr2.to_4x4()
    mm2[0][3] = loc[0]
    mm2[1][3] = loc[1]
    mm2[2][3] = loc[2]

    dist0 = (loc-location).length
    r3d.view_distance = dist0
    r3d.view_matrix = mm2.inverted()
def add_f_curve_modifiers(armature_object, strength, speed):
    wind_vector = Vector((1, 0, 0)) * strength

    fcurves = armature_object.animation_data.action.fcurves
    for f in fcurves:
        for m in f.modifiers:
            f.modifiers.remove(m)

    bones = organize_bones(armature_object)

    for b in bones:
        mass = b.bone.tail_radius ** 2 * b.length
        barycenter = b.tail * mass
        for c in b.children:
            mass += c["mass"]
            barycenter += Vector(c["barycenter"])
        b["mass"] = mass
        b["barycenter"] = barycenter
        barycenter /= mass

        b.rotation_mode = 'XYZ'
        b.keyframe_insert('rotation_euler', frame=0, index=0)
        b.keyframe_insert('rotation_euler', frame=0, index=2)

    fcurves = armature_object.animation_data.action.fcurves


    for i in range(len(bones)):
        f0 = fcurves[2 * i]
        f1 = fcurves[2 * i + 1]
        b = bones[i]

        i_base = b.matrix.to_3x3().inverted()
        bone_vector = b.tail - b.head

        inertia_moment = bone_vector.length ** 2 * bones[i]["mass"] / 10000
        damping = 0.5 * b.bone.tail_radius
        stiffness = b.bone.tail_radius ** 2 / b.length * 800
        if b.parent is not None and len(b.parent.children) > 1:
            stiffness *= 2
            # torque /= 3
        # else:
        #     torque = Vector((0, 0, 0))
        torque = i_base * wind_vector.cross(bone_vector) / (b.bone.tail_radius) / 1000

        f = sqrt(abs(damping ** 2 - 4 * inertia_moment * stiffness)) / (5*b.bone.tail_radius) * speed

        x_amplitude = torque.x
        z_amplitude = torque.z

        m0 = f0.modifiers.new(type='FNGENERATOR')
        m1 = f1.modifiers.new(type='FNGENERATOR')
        m0.function_type = 'SIN'
        m1.function_type = 'SIN'
        m0.amplitude = x_amplitude
        m1.amplitude = z_amplitude
        m0.phase_multiplier = f
        m1.phase_multiplier = f
        m0.value_offset = x_amplitude * 3
        m1.value_offset = z_amplitude * 3
Example #8
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()
Example #9
0
def get_align_matrix(location, normal):
    up = Vector((0, 0, 1))
    angle = normal.angle(up)
    axis = up.cross(normal)
    mat_rot = Matrix.Rotation(angle, 4, axis)
    mat_loc = Matrix.Translation(location)
    mat_align = mat_rot * mat_loc
    return mat_align
Example #10
0
def find_norm_vectors(mesh, vert_dict):
    vertices = list(vert_dict.keys())
    for vert in vertices:
        edge_vectors = []
        for edge in vert_dict[vert].edges:
            if vert == edge[0]:
                vec = mesh.vertices[edge[0]].co.copy() - mesh.vertices[edge[1]].co.copy()
            else:
                vec = mesh.vertices[edge[1]].co.copy() - mesh.vertices[edge[0]].co.copy()
            edge_vectors.append(vec/vec.length)
        vec1 = Vector.cross(edge_vectors[0], edge_vectors[1])
        edges_vec = edge_vectors[1] + edge_vectors[0]
        norm_vec = Vector.cross(vec1, edges_vec)
        if norm_vec.length < 1e-12:           #hackish fix for flat surfaces and straight edges
            norm_vec = edge_vectors[0] 
        
        vert_dict[vert].new_index = len(mesh.vertices) + vertices.index(vert)
        vert_dict[vert].norm_vec = norm_vec
        vert_dict[vert].edge_vec = edge_vectors[0]
def set_bonded(atom,b):
    """Sets the positions of all atoms bonded to atom.
    
    atom: Atom object
    rotate: Boolean defining whether or not to rotate tetrahedron around the z-axis
    b: bond length
    """
    x = 0
    y = 0
    z = 0
    b = b*sqrt(atom.radius)
    
    bonds = atom.bonds
    
    #Differences in position between central atom and bonded atom
    for item in bonds:
        if isinstance(item, Atom):
            x = atom.pos[0] - item.pos[0]
            y = atom.pos[1] - item.pos[1]
            z = atom.pos[2] - item.pos[2]
            break
   
    n = math.radians(109.5)
    n2 = pi*2/3

    #Represents tetrahedron base centered at (0,0,0)
    v1 = Vector((b*sin(n),0,b*cos(n)))
    v2 = Vector((b*cos(n2),b*sin(n2),b*cos(n)))
    v3 = Vector((b*cos(2*n2),b*sin(2*n2),b*cos(n)))
    
    offset = (Vector((atom.pos)))

    top= Vector((0,0,-b))
    diff = Vector((x,y,z))

    axis = top.cross(diff)
    a = -(top.angle(diff, 0))
    
    m = Matrix.Rotation(a,3,axis)
    #Rotates the tetrahedron base to align itself with the starting vector
    v1 = v1*m
    v2 = v2*m
    v3 = v3*m
    
    #Sets the positions of bonded atoms to locations in the tetrahedron, scales bond lengths and moves tetrahedron
    if len(bonds) >1:
        if isinstance(bonds[1], Atom):
            bonds[1].pos = v1*sqrt(bonds[1].radius)+offset
    if len(bonds) >2:
        if isinstance(bonds[2], Atom):
            bonds[2].pos = v2*sqrt(bonds[2].radius)+offset
    if len(bonds)>3:
        if isinstance(bonds[3],Atom):
            bonds[3].pos = v3*sqrt(bonds[3].radius)+offset
Example #12
0
def make_strut(v1, v2, id, od, n, solid, loops):
    v1 = Vector(v1)
    v2 = Vector(v2)
    axis = v2 - v1
    pos = [(0, od / 2)]
    if loops:
        pos += [((od - id) / 2, od / 2),
                (axis.length - (od - id) / 2, od / 2)]
    pos += [(axis.length, od / 2)]
    if solid:
        pos += [(axis.length, id / 2)]
        if loops:
            pos += [(axis.length - (od - id) / 2, id / 2),
                    ((od - id) / 2, id / 2)]
        pos += [(0, id / 2)]
    vps = len(pos)
    fps = vps
    if not solid:
        fps -= 1
    fw = axis.copy()
    fw.normalize()
    if (abs(axis[0] / axis.length) < 1e-5
        and abs(axis[1] / axis.length) < 1e-5):
        up = Vector((-1, 0, 0))
    else:
        up = Vector((0, 0, 1))
    lf = up.cross(fw)
    lf.normalize()
    up = fw.cross(lf)
    mat = Matrix((fw, lf, up))
    mat.transpose()
    verts = [None] * n * vps
    faces = [None] * n * fps
    for i in range(n):
        base = (i - 1) * vps
        x = cossin[i][0]
        y = cossin[i][1]
        for j in range(vps):
            p = Vector((pos[j][0], pos[j][1] * x, pos[j][1] * y))
            p = mat * p
            verts[i * vps + j] = p + v1
        if i:
            for j in range(fps):
                f = (i - 1) * fps + j
                faces[f] = [base + j, base + vps + j,
                            base + vps + (j + 1) % vps, base + (j + 1) % vps]
    base = len(verts) - vps
    i = n
    for j in range(fps):
        f = (i - 1) * fps + j
        faces[f] = [base + j, j, (j + 1) % vps, base + (j + 1) % vps]
    #print(verts,faces)
    return verts, faces
def calc_average_area(mdl):
    frame = mdl.frames[0]
    if frame.type:
        frame = frame.frames[0]
    totalarea = 0.0
    for tri in mdl.tris:
        verts = tuple(map(lambda i: frame.verts[i], tri.verts))
        a = Vector(verts[0].r) - Vector(verts[1].r)
        b = Vector(verts[2].r) - Vector(verts[1].r)
        c = a.cross(b)
        totalarea += (c * c) ** 0.5 / 2.0
    return totalarea / len(mdl.tris)
    def calcRotAngle(self, axis, nor_x, nor_y, nor_z):
        theta = 0
        vector_z = Vector((0.0, 0.0, 1.0))
        vector_n = None
        vector_cross = None

        if axis == 'X':
            vector_n = Vector((0.0, nor_y, nor_z))
            theta = vector_z.angle(vector_n, 999)
            vector_cross = vector_n.cross(vector_z)
            if vector_cross.x < 0:
                theta = -(theta)
        elif axis == 'Y':
            vector_n = Vector((nor_x, 0.0, nor_z))
            theta = vector_z.angle(vector_n, 999)
            vector_cross = vector_n.cross(vector_z)
            if vector_cross.y < 0:
                theta = -(theta)
        else:
            pass

        return theta
Example #15
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
Example #16
0
def convert_bump(pixels, width, height):
    outp = list(pixels)
    for y in range(1, height - 1):
        for x in range(1, width - 1):
            i = ((y * width + x) * 4,
                 (y * width + x - 1) * 4,
                 (y * width + x + 1) * 4,
                 ((y - 1) * width + x) * 4,
                 ((y + 1) * width + x) * 4)
            dx = Vector((1, 0, (pixels[i[2]] - pixels[i[1]]) / 2.0))
            dy = Vector((0, 1, (pixels[i[4]] - pixels[i[3]]) / 2.0))
            n = dx.cross(dy)
            n.normalize()
            outp[i[0]:i[0]+3] = map(lambda x: int(x * 127) + 128, list(n))
    return outp
Example #17
0
File: fkik.py Project: jultrunb/ass
def matchPoleTarget(pb, above, below):
    x = Vector(above.matrix.col[1][:3])
    y = Vector(below.matrix.col[1][:3])
    p0 = Vector(below.matrix.col[3][:3])
    n = x.cross(y)
    if abs(n.length) > 1e-4:
        z = x - y
        n.normalize()
        z -= z.dot(n)*n
        z.normalize()
        p = p0 + 6*pb.length*z
    else:
        p = p0
    gmat = Matrix.Translation(p)
    pmat = getPoseMatrix(gmat, pb)
    insertLocation(pb, pmat)
Example #18
0
def fencehop():
	cont = bge.logic.getCurrentController()
	own = cont.owner
	
	auto_action = cont.sensors['auto_action']
	
	if not auto_action:
		return False
	elif own['isSpaceon']:
		#Gather info on the fence
		fence_obj = auto_action.hitObject
		fence_pos = fence_obj.worldPosition
		
		#get the normal of the fence
		hit_norm = Vector(auto_action.hitNormal)
		hit_norm.z = 0.0
		
		#Vector axis of Sintel
		own_negative_y = Vector(own.getAxisVect((0.0, -1.0, 0.0)))
		own_negative_y.z = 0.0
		
		#Cross it, then get the absolute vaule
		cross = hit_norm.cross(own_negative_y)
		cross = math.fabs(cross[2])
		
		#print (cross)
		
		#Check the angle of approach
		if cross <=.5:
			new_pos = (fence_pos[2] + 2)
			own.worldPosition[2] = new_pos
			
			own['Leaping']=True
			CURRENT_SPEED = own.getLinearVelocity(True)
			
			
			if CURRENT_SPEED[1] < 11:
				CURRENT_SPEED[1] = 11
				
			CURRENT_SPEED[2] = 10
			#print (CURRENT_SPEED)
			own.setLinearVelocity(CURRENT_SPEED ,True)
			own['Tracking']=False
			return True
		else:
			return False
Example #19
0
File: fkik.py Project: 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)
Example #20
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
Example #21
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)
Example #22
0
def main():
	cont = bge.logic.getCurrentController()
	own = cont.owner

	wall_ray = cont.sensors["wall_ray"]

	wall_normal = Vector(wall_ray.hitNormal)
	wall_normal.z = 0.0

	own_negative_y = Vector(own.getAxisVect((0.0, -1.0, 0.0)))
	own_negative_y.z = 0.0
				
	cross = wall_normal.cross(own_negative_y)

	if cross.z > 0.0:
		new_dir = Matrix.Rotation(-90.0, 3, 'X') * wall_normal
	else:
		new_dir = Matrix.Rotation(90.0, 3, 'X') * wall_normal
	
	#print (new_dir)
	#own.alignAxisToVect(new_dir, 0, .1)
Example #23
0
def trackball(p1x, p1y, p2x, p2y, TRACKBALLSIZE=1.0):
    #"""
    #if (p1x == p2x) and (p1y == p2y):
    #    return Quaternion() # Zero rotation
    
    # First, figure out z-coordinates for projection of P1 and P2 to deformed sphere
    p1 = Vector((p1x, p1y, tb_project_to_sphere(TRACKBALLSIZE, p1x, p1y)))
    p2 = Vector((p2x, p2y, tb_project_to_sphere(TRACKBALLSIZE, p2x, p2y)))
    
    # Now, we want the cross product of P1 and P2
    a = p2.cross(p1) # vcross(p2,p1,a); # Axis of rotation
    
    # Figure out how much to rotate around that axis.
    d = p1 - p2
    t = d.magnitude / (2.0*TRACKBALLSIZE)
    
    # Avoid problems with out-of-control values...
    t = min(max(t, -1.0), 1.0)
    #phi = 2.0 * math.asin(t) # how much to rotate about axis
    phi = 2.0 * t # how much to rotate about axis
    
    return Quaternion(a, phi)
Example #24
0
	def set_orientation(self, ori, local=False):
		vector = True
		try:
			len(ori[0])
			vector = False
		except TypeError:
			pass
		if vector:
			ori_vec = [float(i) for i in ori]
			y = Vector((ori_vec[0], ori_vec[1], ori_vec[2]))
			z = Vector((0.0, 0.0, 1.0))
			x = y.cross(z)
			ori = ([
						[x[0], y[0], z[0]],
						[x[1], y[1], z[1]],
						[x[2], y[2], z[2]]
						])
	
		if local:
			self.gameobj.localOrientation = ori[:]
		else:
			self.gameobj.worldOrientation = ori[:]
    def modal_rotate_tool(self, context, eventd):
        cx,cy = self.action_center
        mx,my = eventd['mouse']
        px,py = self.prev_pos #mode_pos

        if eventd['press'] in {'RET', 'NUMPAD_ENTER', 'LEFTMOUSE'}:
            self.tool_fn('commit', eventd)
            return 'main'

        if eventd['press'] in {'ESC', 'RIGHTMOUSE'}:
            self.tool_fn('undo', eventd)
            return 'main'

        if eventd['type'] == 'MOUSEMOVE':
            vp = Vector((px-cx,py-cy,0))
            vm = Vector((mx-cx,my-cy,0))
            ang = vp.angle(vm) * (-1 if vp.cross(vm).z<0 else 1)
            self.tool_rot += ang
            self.tool_fn(self.tool_rot, eventd)
            self.prev_pos = (mx,my)
            return ''

        return ''
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
Example #27
0
def camera_light_source(use_camera, use_light, object_center_vec, object_size):

    camera_factor = 15.0

    # If chosen a camera is put into the scene.
    if use_camera == True:

        # Assume that the object is put into the global origin. Then, the
        # camera is moved in x and z direction, not in y. The object has its
        # size at distance sqrt(object_size) from the origin. So, move the
        # camera by this distance times a factor of camera_factor in x and z.
        # Then add x, y and z of the origin of the object.
        object_camera_vec = Vector((sqrt(object_size) * camera_factor, 0.0,
                                    sqrt(object_size) * camera_factor))
        camera_xyz_vec = object_center_vec + object_camera_vec

        # Create the camera
        camera_data = bpy.data.cameras.new("A_camera")
        camera_data.lens = 45
        camera_data.clip_end = 500.0
        camera = bpy.data.objects.new("A_camera", camera_data)
        camera.location = camera_xyz_vec
        bpy.context.collection.objects.link(camera)

        # Here the camera is rotated such it looks towards the center of
        # the object. The [0.0, 0.0, 1.0] vector along the z axis
        z_axis_vec = Vector((0.0, 0.0, 1.0))
        # The angle between the last two vectors
        angle = object_camera_vec.angle(z_axis_vec, 0)
        # The cross-product of z_axis_vec and object_camera_vec
        axis_vec = z_axis_vec.cross(object_camera_vec)
        # Rotate 'axis_vec' by 'angle' and convert this to euler parameters.
        # 4 is the size of the matrix.
        camera.rotation_euler = Matrix.Rotation(angle, 4, axis_vec).to_euler()

        # Rotate the camera around its axis by 90° such that we have a nice
        # camera position and view onto the object.
        bpy.ops.object.select_all(action='DESELECT')
        camera.select_set(True)

        # Rotate the camera around its axis 'object_camera_vec' by 90° such
        # that we have a nice camera view onto the object.
        matrix_rotation = Matrix.Rotation(90 / 360 * 2 * pi, 4,
                                          object_camera_vec)
        rotate_object(matrix_rotation, camera)

    # Here a lamp is put into the scene, if chosen.
    if use_light == True:

        # This is the distance from the object measured in terms of %
        # of the camera distance. It is set onto 50% (1/2) distance.
        lamp_dl = sqrt(object_size) * 15 * 0.5
        # This is a factor to which extend the lamp shall go to the right
        # (from the camera  point of view).
        lamp_dy_right = lamp_dl * (3.0 / 4.0)

        # Create x, y and z for the lamp.
        object_lamp_vec = Vector((lamp_dl, lamp_dy_right, lamp_dl))
        lamp_xyz_vec = object_center_vec + object_lamp_vec
        length = lamp_xyz_vec.length

        # As a lamp we use a point source.
        lamp_data = bpy.data.lights.new(name="A_lamp", type="POINT")
        # We now determine the emission strength of the lamp. Note that the
        # intensity depends on 1/r^2. For this we use a value of 100000.0 at a
        # distance of 58. This value was determined manually inside Blender.
        lamp_data.energy = 500000.0 * ((length * length) / (58.0 * 58.0))
        lamp = bpy.data.objects.new("A_lamp", lamp_data)
        lamp.location = lamp_xyz_vec
        bpy.context.collection.objects.link(lamp)

        # Some settings for the World: a bit ambient occlusion
        bpy.context.scene.world.light_settings.use_ambient_occlusion = True
        bpy.context.scene.world.light_settings.ao_factor = 0.1
Example #28
0
    def read(self):
        tree = et.parse(self.filepath)

        # Clear the scene
        for ob in bpy.context.scene.objects:
            ob.select = True
        bpy.ops.object.delete()

        camera = tree.find("./camera")
        bpy.ops.object.camera_add()
        camera_obj = bpy.context.selected_objects[0]
        nearClip = 1e-4
        farClip = 1e4
        fov = 30.0
        width = 1280
        height = 720
        for parameter in camera:
            name = parameter.get('name')
            if name == 'nearClip':
                nearClip = float(parameter.get('value'))
            elif name == 'farClip':
                farClip = float(parameter.get('value'))
            elif name == 'fov':
                fov = float(parameter.get('value'))
            elif name == 'width':
                width = int(parameter.get('value'))
            elif name == 'height':
                height = int(parameter.get('value'))
            elif name == 'toWorld':
                lookat = parameter.find("./lookat")
                target = tuple(
                    float(x) for x in self.__str2float(lookat.get('target')))
                origin = tuple(
                    float(x) for x in self.__str2float(lookat.get('origin')))
                up = tuple(
                    float(x) for x in self.__str2float(lookat.get('up')))

                vtarget = Vector(target)
                vorigin = Vector(origin)
                vup = Vector(up)

                vdir = vtarget - vorigin
                vdir.normalize()
                vup.normalize()
                vleft = vup.cross(vdir)
                vleft.normalize()
                newup = vdir.cross(vleft)
                newup.normalize()

                lookat_transform = Matrix([
                    (vleft.x, newup.x, vdir.x, vorigin.x),
                    (vleft.y, newup.y, vdir.y, vorigin.y),
                    (vleft.z, newup.z, vdir.z, vorigin.z), (0, 0, 0, 1)
                ])
                euler = lookat_transform.to_euler()
                euler.z = euler.z
                camera_obj.location = vorigin
                camera_obj.rotation_euler = euler

        camera_obj.data.clip_end = farClip
        camera_obj.data.clip_start = nearClip
        camera_obj.data.lens_unit = 'FOV'
        camera_obj.data.angle = fov / 180.0 * math.pi
        bpy.context.scene.render.resolution_x = width
        bpy.context.scene.render.resolution_y = height

        camera_obj.select = False
        meshes = tree.findall("./mesh")

        # import meshes
        for mesh in meshes:
            obj = mesh.find("./string[@name='filename']")
            obj_path = obj.get("value")

            bpy.ops.import_scene.obj(
                filepath=os.path.join(self.workingDir, obj_path))
            me = bpy.context.selected_objects[0].data

            bm = bmesh.new()
            bm.from_mesh(me)
            bmesh.ops.scale(bm, vec=(1, -1, -1))

            transforms = mesh.find("./transform")
            for transform in transforms:
                transform_type = transform.tag
                values = [
                    float(x)
                    for x in re.split('[,\s]+', transform.get('value'))
                ]
                if transform_type == 'matrix':
                    bmatrix = list(zip(*([iter(values)] * 4)))
                    bmesh.ops.transform(bm, matrix=Matrix(bmatrix))
                elif transform_type == 'scale':
                    bmesh.ops.scale(bm, vec=values)
                elif transform_type == 'translate':
                    bmesh.ops.translate(bm, vec=values)

            bm.to_mesh(me)
            bm.free()

            mat = bpy.data.materials.new(
                name=bpy.context.selected_objects[0].name)

            bsdf = mesh.find("./bsdf")
            bsdf_type = bsdf.get("type")
            if bsdf_type == 'diffuse':
                albedo = bsdf.find("./color[@name='albedo']")
                color = tuple(
                    float(x) for x in self.__str2float(albedo.get('value')))
                bpy.context.selected_objects[0].data.materials.append(mat)
                mat.diffuse_color = color

            emitter = mesh.find("./emitter")
            if emitter is not None:
                radiance = emitter.find("./color[@name='radiance']")
                color = max(
                    float(x)
                    for x in re.split('[,\s]+', radiance.get('value')))
                mat.emit = color / 1000

            bpy.context.selected_objects[0].select = False
Example #29
0
class GlLine(GlBaseLine):
    """
        2d/3d Line
    """
    def __init__(self, d=3, p=None, v=None, p0=None, p1=None, z_axis=None):
        """
            d=3 use 3d coords, d=2 use 2d pixels coords
            Init by either
            p: Vector or tuple origin
            v: Vector or tuple size and direction
            or
            p0: Vector or tuple 1 point location
            p1: Vector or tuple 2 point location
            Will convert any into Vector 3d
            both optionnals
        """
        if p is not None and v is not None:
            self.p = Vector(p)
            self.v = Vector(v)
        elif p0 is not None and p1 is not None:
            self.p = Vector(p0)
            self.v = Vector(p1) - self.p
        else:
            self.p = Vector((0, 0, 0))
            self.v = Vector((0, 0, 0))
        if z_axis is not None:
            self.z_axis = z_axis
        else:
            self.z_axis = Vector((0, 0, 1))
        GlBaseLine.__init__(self, d)

    @property
    def p0(self):
        return self.p

    @property
    def p1(self):
        return self.p + self.v

    @p0.setter
    def p0(self, p0):
        """
            Note: setting p0
            move p0 only
        """
        p1 = self.p1
        self.p = Vector(p0)
        self.v = p1 - p0

    @p1.setter
    def p1(self, p1):
        """
            Note: setting p1
            move p1 only
        """
        self.v = Vector(p1) - self.p

    @property
    def length(self):
        return self.v.length

    @property
    def angle(self):
        return atan2(self.v.y, self.v.x)

    @property
    def cross(self):
        """
            Vector perpendicular on plane defined by z_axis
            lie on the right side
            p1
            |--x
            p0
        """
        return self.v.cross(self.z_axis)

    def normal(self, t=0):
        """
            Line perpendicular on plane defined by z_axis
            lie on the right side
            p1
            |--x
            p0
        """
        n = GlLine()
        n.p = self.lerp(t)
        n.v = self.cross
        return n

    def sized_normal(self, t, size):
        """
            GlLine perpendicular on plane defined by z_axis and of given size
            positionned at t in current line
            lie on the right side
            p1
            |--x
            p0
        """
        n = GlLine()
        n.p = self.lerp(t)
        n.v = size * self.cross.normalized()
        return n

    def lerp(self, t):
        """
            Interpolate along segment
            t parameter [0, 1] where 0 is start of arc and 1 is end
        """
        return self.p + self.v * t

    def offset(self, offset):
        """
            offset > 0 on the right part
        """
        self.p += offset * self.cross.normalized()

    def point_sur_segment(self, pt):
        """ point_sur_segment (2d)
            point: Vector 3d
            t: param t de l'intersection sur le segment courant
            d: distance laterale perpendiculaire positif a droite
        """
        dp = (pt - self.p).to_2d()
        v2d = self.v.to_2d()
        dl = v2d.length
        d = (self.v.x * dp.y - self.v.y * dp.x) / dl
        t = (v2d * dp) / (dl * dl)
        return t > 0 and t < 1, d, t

    @property
    def pts(self):
        return [self.p0, self.p1]
def extract_primitives(glTF, blender_mesh, blender_object,
                       blender_vertex_groups, modifiers, export_settings):
    """
    Extract primitives from a mesh. Polygons are triangulated and sorted by material.

    Furthermore, primitives are split up, if the indices range is exceeded.
    Finally, triangles are also split up/duplicated, if face normals are used instead of vertex normals.
    """
    print_console('INFO', 'Extracting primitive: ' + blender_mesh.name)

    if blender_mesh.has_custom_normals:
        # Custom normals are all (0, 0, 0) until calling calc_normals_split() or calc_tangents().
        blender_mesh.calc_normals_split()

    use_tangents = False
    if blender_mesh.uv_layers.active and len(blender_mesh.uv_layers) > 0:
        try:
            blender_mesh.calc_tangents()
            use_tangents = True
        except Exception:
            print_console(
                'WARNING',
                'Could not calculate tangents. Please try to triangulate the mesh first.'
            )

    #

    material_map = {}

    #
    # Gathering position, normal and tex_coords.
    #
    no_material_attributes = {POSITION_ATTRIBUTE: [], NORMAL_ATTRIBUTE: []}

    if use_tangents:
        no_material_attributes[TANGENT_ATTRIBUTE] = []

    #
    # Directory of materials with its primitive.
    #
    no_material_primitives = {
        MATERIAL_ID: 0,
        INDICES_ID: [],
        ATTRIBUTES_ID: no_material_attributes
    }

    material_idx_to_primitives = {0: no_material_primitives}

    #

    vertex_index_to_new_indices = {}

    material_map[0] = vertex_index_to_new_indices

    #
    # Create primitive for each material.
    #
    for (mat_idx, _) in enumerate(blender_mesh.materials):
        attributes = {POSITION_ATTRIBUTE: [], NORMAL_ATTRIBUTE: []}

        if use_tangents:
            attributes[TANGENT_ATTRIBUTE] = []

        primitive = {
            MATERIAL_ID: mat_idx,
            INDICES_ID: [],
            ATTRIBUTES_ID: attributes
        }

        material_idx_to_primitives[mat_idx] = primitive

        #

        vertex_index_to_new_indices = {}

        material_map[mat_idx] = vertex_index_to_new_indices

    tex_coord_max = 0
    if blender_mesh.uv_layers.active:
        tex_coord_max = len(blender_mesh.uv_layers)

    #

    vertex_colors = {}

    color_index = 0
    for vertex_color in blender_mesh.vertex_colors:
        vertex_color_name = COLOR_PREFIX + str(color_index)
        vertex_colors[vertex_color_name] = vertex_color

        color_index += 1
        if color_index >= GLTF_MAX_COLORS:
            break
    color_max = color_index

    #

    bone_max = 0
    for blender_polygon in blender_mesh.polygons:
        for loop_index in blender_polygon.loop_indices:
            vertex_index = blender_mesh.loops[loop_index].vertex_index
            bones_count = len(blender_mesh.vertices[vertex_index].groups)
            if bones_count > 0:
                if bones_count % 4 == 0:
                    bones_count -= 1
                bone_max = max(bone_max, bones_count // 4 + 1)

    #

    morph_max = 0

    blender_shape_keys = []

    if blender_mesh.shape_keys is not None:
        for blender_shape_key in blender_mesh.shape_keys.key_blocks:
            if blender_shape_key != blender_shape_key.relative_key:
                if blender_shape_key.mute is False:
                    morph_max += 1
                    blender_shape_keys.append(
                        ShapeKey(
                            blender_shape_key,
                            blender_shape_key.normals_vertex_get(
                            ),  # calculate vertex normals for this shape key
                            blender_shape_key.normals_polygon_get())
                    )  # calculate polygon normals for this shape key

    armature = None
    if modifiers is not None:
        modifiers_dict = {m.type: m for m in modifiers}
        if "ARMATURE" in modifiers_dict:
            modifier = modifiers_dict["ARMATURE"]
            armature = modifier.object

    #
    # Convert polygon to primitive indices and eliminate invalid ones. Assign to material.
    #
    for blender_polygon in blender_mesh.polygons:
        export_color = True

        #

        if export_settings['gltf_materials'] is False:
            primitive = material_idx_to_primitives[0]
            vertex_index_to_new_indices = material_map[0]
        elif not blender_polygon.material_index in material_idx_to_primitives:
            primitive = material_idx_to_primitives[0]
            vertex_index_to_new_indices = material_map[0]
        else:
            primitive = material_idx_to_primitives[
                blender_polygon.material_index]
            vertex_index_to_new_indices = material_map[
                blender_polygon.material_index]
        #

        attributes = primitive[ATTRIBUTES_ID]

        face_normal = blender_polygon.normal
        face_tangent = Vector((0.0, 0.0, 0.0))
        face_bitangent = Vector((0.0, 0.0, 0.0))
        if use_tangents:
            for loop_index in blender_polygon.loop_indices:
                temp_vertex = blender_mesh.loops[loop_index]
                face_tangent += temp_vertex.tangent
                face_bitangent += temp_vertex.bitangent

            face_tangent.normalize()
            face_bitangent.normalize()

        #

        indices = primitive[INDICES_ID]

        loop_index_list = []

        if len(blender_polygon.loop_indices) == 3:
            loop_index_list.extend(blender_polygon.loop_indices)
        elif len(blender_polygon.loop_indices) > 3:
            # Triangulation of polygon. Using internal function, as non-convex polygons could exist.
            polyline = []

            for loop_index in blender_polygon.loop_indices:
                vertex_index = blender_mesh.loops[loop_index].vertex_index
                v = blender_mesh.vertices[vertex_index].co
                polyline.append(Vector((v[0], v[1], v[2])))

            triangles = tessellate_polygon((polyline, ))

            for triangle in triangles:

                for triangle_index in triangle:
                    loop_index_list.append(
                        blender_polygon.loop_indices[triangle_index])
        else:
            continue

        for loop_index in loop_index_list:
            vertex_index = blender_mesh.loops[loop_index].vertex_index

            if vertex_index_to_new_indices.get(vertex_index) is None:
                vertex_index_to_new_indices[vertex_index] = []

            #

            v = None
            n = None
            t = None
            b = None
            uvs = []
            colors = []
            joints = []
            weights = []

            target_positions = []
            target_normals = []
            target_tangents = []

            vertex = blender_mesh.vertices[vertex_index]

            v = convert_swizzle_location(vertex.co, armature, blender_object,
                                         export_settings)
            if blender_polygon.use_smooth or blender_mesh.use_auto_smooth:
                if blender_mesh.has_custom_normals:
                    n = convert_swizzle_normal(
                        blender_mesh.loops[loop_index].normal, armature,
                        blender_object, export_settings)
                else:
                    n = convert_swizzle_normal(vertex.normal, armature,
                                               blender_object, export_settings)
                if use_tangents:
                    t = convert_swizzle_tangent(
                        blender_mesh.loops[loop_index].tangent, armature,
                        blender_object, export_settings)
                    b = convert_swizzle_location(
                        blender_mesh.loops[loop_index].bitangent, armature,
                        blender_object, export_settings)
            else:
                n = convert_swizzle_normal(face_normal, armature,
                                           blender_object, export_settings)
                if use_tangents:
                    t = convert_swizzle_tangent(face_tangent, armature,
                                                blender_object,
                                                export_settings)
                    b = convert_swizzle_location(face_bitangent, armature,
                                                 blender_object,
                                                 export_settings)

            if use_tangents:
                tv = Vector((t[0], t[1], t[2]))
                bv = Vector((b[0], b[1], b[2]))
                nv = Vector((n[0], n[1], n[2]))

                if (nv.cross(tv)).dot(bv) < 0.0:
                    t[3] = -1.0

            if blender_mesh.uv_layers.active:
                for tex_coord_index in range(0, tex_coord_max):
                    uv = blender_mesh.uv_layers[tex_coord_index].data[
                        loop_index].uv
                    uvs.append([uv.x, 1.0 - uv.y])

            #

            if color_max > 0 and export_color:
                for color_index in range(0, color_max):
                    color_name = COLOR_PREFIX + str(color_index)
                    color = vertex_colors[color_name].data[loop_index].color
                    colors.append([
                        color_srgb_to_scene_linear(color[0]),
                        color_srgb_to_scene_linear(color[1]),
                        color_srgb_to_scene_linear(color[2]), color[3]
                    ])

            #

            bone_count = 0

            if blender_vertex_groups is not None and vertex.groups is not None and len(
                    vertex.groups) > 0 and export_settings[
                        gltf2_blender_export_keys.SKINS]:
                joint = []
                weight = []
                vertex_groups = vertex.groups
                if not export_settings['gltf_all_vertex_influences']:
                    # sort groups by weight descending
                    vertex_groups = sorted(vertex.groups,
                                           key=attrgetter('weight'),
                                           reverse=True)
                for group_element in vertex_groups:

                    if len(joint) == 4:
                        bone_count += 1
                        joints.append(joint)
                        weights.append(weight)
                        joint = []
                        weight = []

                    #

                    joint_weight = group_element.weight
                    if joint_weight <= 0.0:
                        continue

                    #

                    vertex_group_index = group_element.group
                    vertex_group_name = blender_vertex_groups[
                        vertex_group_index].name

                    joint_index = None

                    if armature:
                        skin = gltf2_blender_gather_skins.gather_skin(
                            armature, export_settings)
                        for index, j in enumerate(skin.joints):
                            if j.name == vertex_group_name:
                                joint_index = index
                                break

                    #
                    if joint_index is not None:
                        joint.append(joint_index)
                        weight.append(joint_weight)

                if len(joint) > 0:
                    bone_count += 1

                    for fill in range(0, 4 - len(joint)):
                        joint.append(0)
                        weight.append(0.0)

                    joints.append(joint)
                    weights.append(weight)

            for fill in range(0, bone_max - bone_count):
                joints.append([0, 0, 0, 0])
                weights.append([0.0, 0.0, 0.0, 0.0])

            #

            if morph_max > 0 and export_settings[
                    gltf2_blender_export_keys.MORPH]:
                for morph_index in range(0, morph_max):
                    blender_shape_key = blender_shape_keys[morph_index]

                    v_morph = convert_swizzle_location(
                        blender_shape_key.shape_key.data[vertex_index].co,
                        armature, blender_object, export_settings)

                    # Store delta.
                    v_morph -= v

                    target_positions.append(v_morph)

                    #

                    n_morph = None

                    if blender_polygon.use_smooth:
                        temp_normals = blender_shape_key.vertex_normals
                        n_morph = (temp_normals[vertex_index * 3 + 0],
                                   temp_normals[vertex_index * 3 + 1],
                                   temp_normals[vertex_index * 3 + 2])
                    else:
                        temp_normals = blender_shape_key.polygon_normals
                        n_morph = (temp_normals[blender_polygon.index * 3 + 0],
                                   temp_normals[blender_polygon.index * 3 + 1],
                                   temp_normals[blender_polygon.index * 3 + 2])

                    n_morph = convert_swizzle_normal(Vector(n_morph), armature,
                                                     blender_object,
                                                     export_settings)

                    # Store delta.
                    n_morph -= n

                    target_normals.append(n_morph)

                    #

                    if use_tangents:
                        rotation = n_morph.rotation_difference(n)

                        t_morph = Vector((t[0], t[1], t[2]))

                        t_morph.rotate(rotation)

                        target_tangents.append(t_morph)

            #
            #

            create = True

            for current_new_index in vertex_index_to_new_indices[vertex_index]:
                found = True

                for i in range(0, 3):
                    if attributes[POSITION_ATTRIBUTE][current_new_index * 3 +
                                                      i] != v[i]:
                        found = False
                        break

                    if attributes[NORMAL_ATTRIBUTE][current_new_index * 3 +
                                                    i] != n[i]:
                        found = False
                        break

                if use_tangents:
                    for i in range(0, 4):
                        if attributes[TANGENT_ATTRIBUTE][current_new_index * 4
                                                         + i] != t[i]:
                            found = False
                            break

                if not found:
                    continue

                for tex_coord_index in range(0, tex_coord_max):
                    uv = uvs[tex_coord_index]

                    tex_coord_id = TEXCOORD_PREFIX + str(tex_coord_index)
                    for i in range(0, 2):
                        if attributes[tex_coord_id][current_new_index * 2 +
                                                    i] != uv[i]:
                            found = False
                            break

                if export_color:
                    for color_index in range(0, color_max):
                        color = colors[color_index]

                        color_id = COLOR_PREFIX + str(color_index)
                        for i in range(0, 3):
                            # Alpha is always 1.0 - see above.
                            current_color = attributes[color_id][
                                current_new_index * 4 + i]
                            if color_srgb_to_scene_linear(
                                    current_color) != color[i]:
                                found = False
                                break

                if export_settings[gltf2_blender_export_keys.SKINS]:
                    for bone_index in range(0, bone_max):
                        joint = joints[bone_index]
                        weight = weights[bone_index]

                        joint_id = JOINTS_PREFIX + str(bone_index)
                        weight_id = WEIGHTS_PREFIX + str(bone_index)
                        for i in range(0, 4):
                            if attributes[joint_id][current_new_index * 4 +
                                                    i] != joint[i]:
                                found = False
                                break
                            if attributes[weight_id][current_new_index * 4 +
                                                     i] != weight[i]:
                                found = False
                                break

                if export_settings[gltf2_blender_export_keys.MORPH]:
                    for morph_index in range(0, morph_max):
                        target_position = target_positions[morph_index]
                        target_normal = target_normals[morph_index]
                        if use_tangents:
                            target_tangent = target_tangents[morph_index]

                        target_position_id = MORPH_POSITION_PREFIX + str(
                            morph_index)
                        target_normal_id = MORPH_NORMAL_PREFIX + str(
                            morph_index)
                        target_tangent_id = MORPH_TANGENT_PREFIX + str(
                            morph_index)
                        for i in range(0, 3):
                            if attributes[target_position_id][
                                    current_new_index * 3 +
                                    i] != target_position[i]:
                                found = False
                                break
                            if attributes[target_normal_id][
                                    current_new_index * 3 +
                                    i] != target_normal[i]:
                                found = False
                                break
                            if use_tangents:
                                if attributes[target_tangent_id][
                                        current_new_index * 3 +
                                        i] != target_tangent[i]:
                                    found = False
                                    break

                if found:
                    indices.append(current_new_index)

                    create = False
                    break

            if not create:
                continue

            new_index = 0

            if primitive.get('max_index') is not None:
                new_index = primitive['max_index'] + 1

            primitive['max_index'] = new_index

            vertex_index_to_new_indices[vertex_index].append(new_index)

            #
            #

            indices.append(new_index)

            #

            attributes[POSITION_ATTRIBUTE].extend(v)
            attributes[NORMAL_ATTRIBUTE].extend(n)
            if use_tangents:
                attributes[TANGENT_ATTRIBUTE].extend(t)

            if blender_mesh.uv_layers.active:
                for tex_coord_index in range(0, tex_coord_max):
                    tex_coord_id = TEXCOORD_PREFIX + str(tex_coord_index)

                    if attributes.get(tex_coord_id) is None:
                        attributes[tex_coord_id] = []

                    attributes[tex_coord_id].extend(uvs[tex_coord_index])

            if export_color:
                for color_index in range(0, color_max):
                    color_id = COLOR_PREFIX + str(color_index)

                    if attributes.get(color_id) is None:
                        attributes[color_id] = []

                    attributes[color_id].extend(colors[color_index])

            if export_settings[gltf2_blender_export_keys.SKINS]:
                for bone_index in range(0, bone_max):
                    joint_id = JOINTS_PREFIX + str(bone_index)

                    if attributes.get(joint_id) is None:
                        attributes[joint_id] = []

                    attributes[joint_id].extend(joints[bone_index])

                    weight_id = WEIGHTS_PREFIX + str(bone_index)

                    if attributes.get(weight_id) is None:
                        attributes[weight_id] = []

                    attributes[weight_id].extend(weights[bone_index])

            if export_settings[gltf2_blender_export_keys.MORPH]:
                for morph_index in range(0, morph_max):
                    target_position_id = MORPH_POSITION_PREFIX + str(
                        morph_index)

                    if attributes.get(target_position_id) is None:
                        attributes[target_position_id] = []

                    attributes[target_position_id].extend(
                        target_positions[morph_index])

                    target_normal_id = MORPH_NORMAL_PREFIX + str(morph_index)

                    if attributes.get(target_normal_id) is None:
                        attributes[target_normal_id] = []

                    attributes[target_normal_id].extend(
                        target_normals[morph_index])

                    if use_tangents:
                        target_tangent_id = MORPH_TANGENT_PREFIX + str(
                            morph_index)

                        if attributes.get(target_tangent_id) is None:
                            attributes[target_tangent_id] = []

                        attributes[target_tangent_id].extend(
                            target_tangents[morph_index])

    #
    # Add non-empty primitives
    #

    result_primitives = [
        primitive for primitive in material_idx_to_primitives.values()
        if len(primitive[INDICES_ID]) != 0
    ]

    print_console('INFO', 'Primitives created: ' + str(len(result_primitives)))

    return result_primitives
Example #31
0
def blend_rec(graph,
              tcoords,
              gcoords,
              colors,
              label,
              node_size=3,
              edge_thickness=0.25):
    # edge obj
    bpy.data.materials.new(name="light_gray")
    bpy.data.materials["light_gray"].diffuse_color = (0.4627450980392157,
                                                      0.4627450980392157,
                                                      0.4627450980392157)
    bpy.data.materials["light_gray"].specular_intensity = 0.5
    # sphere obj
    for color, aa in zip(colors, label):
        bpy.data.materials.new(name=aa)
        bpy.data.materials[aa].diffuse_color = color
        bpy.data.materials[aa].specular_intensity = 0.5

        # Transparency parameters
        bpy.data.materials[aa].use_transparency = True
        bpy.data.materials[aa].transparency_method = "RAYTRACE"
        bpy.data.materials[aa].alpha = 0.95
        bpy.data.materials[aa].raytrace_transparency.fresnel = 0.1
        bpy.data.materials[aa].raytrace_transparency.ior = 1.15

    # sphere rec obj
    bpy.data.materials.new(name="red")
    bpy.data.materials["red"].diffuse_color = (1, 0, 0)
    bpy.data.materials["red"].specular_intensity = 0.5

    # text obj
    text = bpy.data.objects.new("label",
                                bpy.data.curves.new(type="FONT", name="curve"))
    bpy.data.materials.new(name="black")
    bpy.data.materials["black"].diffuse_color = (0, 0, 0)
    bpy.data.materials["black"].specular_intensity = 0.5

    # Set scene, light and alpha_mode
    scene = bpy.context.scene
    scene.render.engine = 'BLENDER_RENDER'  # 'CYCLE'
    scene.render.alpha_mode = 'TRANSPARENT'  # remove background

    area = next(area for area in bpy.context.screen.areas
                if area.type == 'VIEW_3D')
    area.spaces[0].region_3d.view_perspective = 'CAMERA'

    bpy.data.worlds["World"].light_settings.use_ambient_occlusion = True
    bpy.data.worlds["World"].light_settings.samples = 10

    # camera position
    if (len(bpy.data.cameras) == 1):
        obj = bpy.data.objects['Camera']  # bpy.types.Camera

        mean_x = np.mean([el[0] for el in tcoords.values()])
        mean_y = np.mean([el[1] for el in tcoords.values()])
        mean_z = np.mean([el[2] for el in tcoords.values()])

        obj.location.x = mean_x
        obj.location.y = mean_y
        min_x = min([el[0] for el in tcoords.values()])
        max_x = max([el[0] for el in tcoords.values()])
        min_y = min([el[1] for el in tcoords.values()])
        max_y = max([el[1] for el in tcoords.values()])
        obj.location.z = mean_z + max(
            max_x - min_x, max_y - min_y) / (2 * np.tan(np.radians(20) / 2))
        obj.rotation_euler = (0.0, 0.0, np.radians(90))
        obj.keyframe_insert(data_path="location", frame=10.0)

    # 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_uv_sphere_add()
    gsphere = bpy.context.object
    gsphere.active_material = bpy.data.materials["red"]

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

    # Draw nodes
    nx.set_node_attributes(graph, [], "color")
    for node, aa in zip(graph.nodes(), label):
        # Coloring rule for nodes. Edit this to suit your needs!
        col = colors[node]

        # 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 = tcoords[node]
        node_sphere.dimensions = [node_size] * 3
        node_sphere.active_material = bpy.data.materials[aa]
        bpy.context.scene.objects.link(node_sphere)
        shapes.append(node_sphere)
        shapes_to_smooth.append(node_sphere)

        #lbl = text.copy()
        #lbl.data = text.data.copy()
        #lbl.data.body = label[node]
        #lbl.rotation_mode = "AXIS_ANGLE"
        #lbl.rotation_euler = (0.0, 0.0, np.radians(90))
        #lbl.active_material = bpy.data.materials["black"]
        #lbl.dimensions = [.00000001] * 3
        #lbl.location = np.asarray(tcoords[node]) + [0., 0., node_size/2]
        #bpy.context.scene.objects.link(lbl)

        # guess atom
        gues_sphere = gsphere.copy()
        gues_sphere.data = gsphere.data.copy()
        gues_sphere.dimensions = [node_size] * 3
        gues_sphere.location = gcoords[node]
        gues_sphere.active_material = bpy.data.materials["red"]
        bpy.context.scene.objects.link(gues_sphere)
        shapes.append(gues_sphere)
        shapes_to_smooth.append(gues_sphere)

    # Draw edges
    for source, target in graph.edges():
        # Get source and target locations by drilling down into data structure
        source_loc = tcoords[source]
        target_loc = tcoords[target]

        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)

    # Remove primitive meshes
    bpy.ops.object.select_all(action='DESELECT')
    sphere.select = True
    gsphere.select = True
    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 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()
Example #32
0
class GlHandle(GlPolygon):
    def __init__(self,
                 sensor_size,
                 size,
                 draggable=False,
                 selectable=False,
                 d=3):
        """
            sensor_size : 2d size in pixels of sensor area
            size : 3d size of handle
        """
        GlPolygon.__init__(self, d=d)
        self.colour_active = (1.0, 0.0, 0.0, 1.0)
        self.colour_hover = (1.0, 1.0, 0.0, 1.0)
        self.colour_normal = (1.0, 1.0, 1.0, 1.0)
        self.colour_selected = (0.0, 0.0, 0.7, 1.0)
        self.size = size
        self.sensor_width = sensor_size
        self.sensor_height = sensor_size
        self.pos_3d = Vector((0, 0, 0))
        self.up_axis = Vector((0, 0, 0))
        self.c_axis = Vector((0, 0, 0))
        self.hover = False
        self.active = False
        self.draggable = draggable
        self.selectable = selectable
        self.selected = False

    def set_pos(self, context, pos_3d, direction, normal=Vector((0, 0, 1))):
        self.up_axis = direction.normalized()
        self.c_axis = self.up_axis.cross(normal)
        self.pos_3d = pos_3d
        self.pos_2d = self.position_2d_from_coord(context, self.sensor_center)

    def check_hover(self, pos_2d):
        if self.draggable:
            dp = pos_2d - self.pos_2d
            self.hover = abs(dp.x) < self.sensor_width and abs(
                dp.y) < self.sensor_height

    @property
    def sensor_center(self):
        pts = self.pts
        n = len(pts)
        x, y, z = 0, 0, 0
        for pt in pts:
            x += pt.x
            y += pt.y
            z += pt.z
        return Vector((x / n, y / n, z / n))

    @property
    def pts(self):
        raise NotImplementedError

    @property
    def colour(self):
        if self.render:
            return self.colour_inactive
        elif self.draggable:
            if self.active:
                return self.colour_active
            elif self.hover:
                return self.colour_hover
            elif self.selected:
                return self.colour_selected
            return self.colour_normal
        else:
            return self.colour_inactive
Example #33
0
def tp_insert_mesh_geometry(self, context):
    wm = context.window_manager
    object = wm.TP_Tileable_Mesh_Previews
    obj_list = []
    second_obj = ""

    if bpy.context.mode == 'OBJECT':

        tp_desc_mesh_insert(object)  ###objectlist

    elif bpy.context.mode == 'EDIT':
        bpy.ops.object.mode_set(mode='OBJECT')
        ref_obj = bpy.context.active_object

        if len(context.selected_objects) == 2:
            obj1, obj2 = context.selected_objects
            second_obj = obj1 if obj2 == ref_obj else obj2

            bpy.data.objects[second_obj.name].select = False

        bpy.ops.object.duplicate_move()
        bpy.context.active_object.name = "Dummy"
        obj = context.active_object
        bpy.ops.object.parent_clear(type='CLEAR_KEEP_TRANSFORM')
        bpy.ops.object.transform_apply(location=True,
                                       rotation=True,
                                       scale=True)

        copy_cursor = bpy.context.scene.cursor_location.copy()

        bm = bmesh.new()
        bm.from_mesh(obj.data)

        selected_faces = [f for f in bm.faces if f.select]

        for face in selected_faces:

            face_location = face.calc_center_median()

            loc_world_space = obj.matrix_world * Vector(face_location)

            z = Vector((0, 0, 1))

            angle = face.normal.angle(z)

            axis = z.cross(face.normal)
            bpy.context.scene.cursor_location = loc_world_space

            tp_desc_mesh_insert(object)  ###objectlist

            bpy.ops.transform.rotate(value=angle, axis=axis)
            obj_list.append(context.object.name)

        bm.to_mesh(obj.data)

        bm.free()

        bpy.context.scene.cursor_location = copy_cursor
        bpy.ops.object.select_all(action='DESELECT')
        bpy.context.scene.objects.active = bpy.data.objects["Dummy"]
        bpy.data.objects["Dummy"].select = True
        bpy.ops.object.delete(use_global=False)

        bpy.context.scene.objects.active = bpy.data.objects[obj_list[0]]

        for obj in obj_list:
            bpy.data.objects[obj].select = True
            bpy.ops.make.link()  # custom link called from operators module

        bpy.ops.object.select_all(action='DESELECT')
        bpy.context.scene.objects.active = bpy.data.objects[ref_obj.name]

        if second_obj:
            bpy.data.objects[second_obj.name].select = True

        bpy.data.objects[ref_obj.name].select = True

        bpy.ops.object.mode_set(mode='EDIT')
        del (obj_list[:])
Example #34
0
    def execute(self, context):

        obj_list = []
        bpy.ops.object.mode_set(mode='OBJECT')
        ref_obj = bpy.context.active_object

        obj1, obj2 = context.selected_objects
        second_obj = obj1 if obj2 == ref_obj else obj2

        obj_list.append(second_obj.name)
        bpy.data.objects[second_obj.name].select = False
        bpy.ops.object.duplicate_move()
        bpy.context.active_object.name = "Dummy"
        obj = context.active_object
        bpy.ops.object.parent_clear(type='CLEAR_KEEP_TRANSFORM')
        bpy.ops.object.transform_apply(location=True,
                                       rotation=True,
                                       scale=True)

        copy_cursor = bpy.context.scene.cursor_location.copy()

        bm = bmesh.new()
        bm.from_mesh(obj.data)

        selected_faces = [f for f in bm.faces if f.select]

        for face in selected_faces:

            face_location = face.calc_center_median()

            loc_world_space = obj.matrix_world * Vector(face_location)

            z = Vector((0, 0, 1))

            angle = face.normal.angle(z)

            axis = z.cross(face.normal)
            bpy.ops.object.select_all(action='DESELECT')
            bpy.context.scene.objects.active = bpy.data.objects[
                second_obj.name]
            bpy.data.objects[second_obj.name].select = True
            bpy.ops.object.duplicate()
            bpy.context.scene.cursor_location = loc_world_space
            bpy.ops.view3d.snap_selected_to_cursor()

            bpy.ops.transform.rotate(value=angle, axis=axis)
            obj_list.append(context.object.name)

        bm.to_mesh(obj.data)

        bm.free()

        bpy.context.scene.cursor_location = copy_cursor
        bpy.ops.object.select_all(action='DESELECT')
        bpy.context.scene.objects.active = bpy.data.objects["Dummy"]
        bpy.data.objects["Dummy"].select = True
        bpy.ops.object.delete(use_global=False)

        bpy.context.scene.objects.active = bpy.data.objects[obj_list[0]]

        for obj in obj_list:
            bpy.data.objects[obj].select = True
            bpy.ops.make.link()  # custom link called from operators module

        bpy.ops.object.select_all(action='DESELECT')
        bpy.context.scene.objects.active = bpy.data.objects[ref_obj.name]
        bpy.data.objects[second_obj.name].select = True
        bpy.data.objects[ref_obj.name].select = True

        bpy.ops.object.mode_set(mode='EDIT')
        del (obj_list[:])

        return {'FINISHED'}
Example #35
0
def create_object_to_selection(self, context):

    if not bpy.context.object:
        add_primitive()
#        bpy.ops.object.orientationvariable(variable="LOCAL")

    elif context.object.mode == 'EDIT':
        #Duplicate the mesh, apply his transform and perform the code to add object on it
        bpy.ops.object.mode_set(mode='OBJECT')
        bpy.ops.object.duplicate_move()
        bpy.context.active_object.name = "Dummy"
        bpy.ops.object.parent_clear(type='CLEAR_KEEP_TRANSFORM')
        bpy.ops.object.transform_apply(location=True,
                                       rotation=True,
                                       scale=True)

        list_insert_meshes = []
        obj = bpy.context.active_object
        saved_location = bpy.context.scene.cursor_location.copy()

        bm = bmesh.new()
        bm.from_mesh(obj.data)

        selected_faces = [f for f in bm.faces if f.select]

        for face in selected_faces:
            face_location = face.calc_center_median()
            loc_world_space = obj.matrix_world * Vector(face_location)
            z = Vector((0, 0, 1))
            angle = face.normal.angle(z)
            axis = z.cross(face.normal)

            bpy.context.scene.cursor_location = loc_world_space

            add_primitive()

            bpy.ops.transform.rotate(value=angle, axis=axis)

            list_insert_meshes.append(context.active_object.name)

        bm.to_mesh(obj.data)
        bm.free()
        bpy.context.scene.cursor_location = saved_location

        #Deselect all the objects, select the dummy object and delete it
        bpy.ops.object.select_all(action='DESELECT')
        bpy.context.scene.objects.active = bpy.data.objects["Dummy"]

        bpy.context.object.select = True
        bpy.ops.object.delete(use_global=False)

        #Select inserted meshes
        for obj in list_insert_meshes:
            bpy.context.scene.objects.active = bpy.data.objects[obj]
            bpy.data.objects[obj].select = True
            if len(list_insert_meshes) > 1:
                bpy.ops.object.make_links_data(type='OBDATA')  #Make link

        del (list_insert_meshes[:])
#        bpy.ops.object.orientationvariable(variable="LOCAL")

    else:
        saved_location = bpy.context.scene.cursor_location.copy()
        bpy.ops.view3d.snap_cursor_to_selected()

        add_primitive()

        bpy.context.scene.cursor_location = saved_location
Example #36
0
    def modal(self, context, event):
        context.area.tag_redraw()

        lin_def_settings = context.scene.mi_ldeformer_settings

        region = context.region
        rv3d = context.region_data
        m_coords = event.mouse_region_x, event.mouse_region_y
        active_obj = context.scene.objects.active
        bm = bmesh.from_edit_mesh(active_obj.data)

        # update check
        if lin_def_settings.manual_update is True:
            if event.type == 'U':
                if event.value == 'PRESS':
                    self.do_update = True
                else:
                    self.do_update = False
        else:
            self.do_update = True

        # tooltip
        tooltip_text = None
        if lin_def_settings.manual_update is True and self.tool_mode not in {
                'IDLE', 'MOVE_POINT'
        }:
            tooltip_text = "Press U key to udate!"
        else:
            tooltip_text = "I:Invert, Z:Z-Constraint, X:X-Constraint, S:Scale, Shift-S:ScaleForward, G:Move, R:Rotate, B:Bend, Shift-B:BendSpiral, T:Tape, Shift-T:Twist, Ctrl+Z:Undo, Ctrl+Shift+Z:Redo"
        context.area.header_text_set(tooltip_text)

        # key pressed
        if self.tool_mode == 'IDLE' and event.value == 'PRESS':
            if event.type in {'LEFTMOUSE', 'SELECTMOUSE'}:
                if self.lw_tool:
                    # pick linear widget point
                    picked_point = l_widget.pick_lw_point(
                        context, m_coords, self.lw_tool)
                    if picked_point:
                        self.deform_mouse_pos = Vector(m_coords)
                        self.active_lw_point = picked_point

                        self.tool_mode = 'MOVE_POINT'
                else:
                    picked_point = ut_base.get_mouse_on_plane(
                        context, self.start_work_center, None, m_coords)
                    if picked_point:
                        self.lw_tool = l_widget.MI_Linear_Widget()

                        self.lw_tool.start_point = l_widget.MI_LW_Point(
                            picked_point.copy())
                        self.lw_tool.middle_point = l_widget.MI_LW_Point(
                            picked_point.copy())
                        self.lw_tool.end_point = l_widget.MI_LW_Point(
                            picked_point)

                        self.active_lw_point = self.lw_tool.end_point

                        self.tool_mode = 'MOVE_POINT'

            elif event.type in {'S', 'G', 'R', 'B', 'T'}:
                # set tool type
                if event.type == 'S':
                    if event.shift:
                        self.tool_mode = 'SCALE_FRONT'
                    else:
                        self.tool_mode = 'SCALE_ALL'
                elif event.type == 'R':
                    self.tool_mode = 'ROTATE_ALL'
                elif event.type == 'G':
                    self.tool_mode = 'MOVE_ALL'
                elif event.type == 'B':
                    if event.shift:
                        self.tool_mode = 'BEND_SPIRAL'
                    else:
                        self.tool_mode = 'BEND_ALL'
                elif event.type == 'T':
                    if event.shift:
                        self.tool_mode = 'TWIST'
                    else:
                        self.tool_mode = 'TAPE'

                # get tool verts
                if self.tool_mode in {'SCALE_FRONT', 'TAPE'}:
                    # do not clamp for SCALE_FRONT mode
                    self.apply_tool_verts = l_widget.get_tool_verts(
                        self.lw_tool, self.work_verts, bm, active_obj, False,
                        True)
                else:
                    self.apply_tool_verts = l_widget.get_tool_verts(
                        self.lw_tool, self.work_verts, bm, active_obj, True,
                        True)

                # set some settings for tools
                if self.tool_mode in {'SCALE_ALL', 'SCALE_FRONT', 'TAPE'}:
                    self.deform_mouse_pos = Vector(m_coords)

                elif self.tool_mode == 'MOVE_ALL':
                    mouse_pos_3d = ut_base.get_mouse_on_plane(
                        context, self.lw_tool.start_point.position, None,
                        m_coords)
                    self.deform_vec_pos = mouse_pos_3d  # 3d location

                elif self.tool_mode in {
                        'ROTATE_ALL', 'TWIST', 'BEND_ALL', 'BEND_SPIRAL'
                }:
                    start_2d = view3d_utils.location_3d_to_region_2d(
                        region, rv3d, self.lw_tool.start_point.position)
                    self.deform_vec_pos = (
                        Vector(m_coords) -
                        start_2d).normalized()  # 2d direction
                    self.deform_mouse_pos = 0.0  # we will use it as angle counter

                    if self.tool_mode in {'BEND_SPIRAL', 'BEND_ALL'}:
                        self.bend_scale_len = (Vector(m_coords) -
                                               start_2d).length

                #return {'RUNNING_MODAL'}

            elif event.type in {'Z', 'X'} and self.lw_tool:
                if event.type == 'Z' and event.ctrl:
                    if event.shift:
                        redo_history(bm, self.h_undo, self.h_redo, active_obj)
                    else:
                        undo_history(bm, self.h_undo, self.h_redo, active_obj)

                else:
                    pre_verts = [bm.verts[v_id] for v_id in self.work_verts]
                    if event.type == 'X':
                        if self.lw_tool_axis:
                            if self.lw_tool_axis == 'X':
                                l_widget.setup_lw_tool(rv3d, self.lw_tool,
                                                       active_obj, pre_verts,
                                                       'X_Left', 1.0)
                                self.lw_tool_axis = 'X_Left'
                            elif self.lw_tool_axis == 'X_Left':
                                l_widget.setup_lw_tool(rv3d, self.lw_tool,
                                                       active_obj, pre_verts,
                                                       'X_Right', 1.0)

                                ## revert direction
                                #stp = self.lw_tool.start_point.position.copy()
                                #self.lw_tool.start_point.position = self.lw_tool.end_point.position
                                #self.lw_tool.end_point.position = stp

                                self.lw_tool_axis = 'X_Right'
                            elif self.lw_tool_axis == 'X_Right':
                                l_widget.setup_lw_tool(rv3d, self.lw_tool,
                                                       active_obj, pre_verts,
                                                       'X', 1.0)
                                self.lw_tool_axis = 'X'
                            else:
                                l_widget.setup_lw_tool(rv3d, self.lw_tool,
                                                       active_obj, pre_verts,
                                                       'X', 1.0)
                                self.lw_tool_axis = 'X'
                        else:
                            l_widget.setup_lw_tool(rv3d, self.lw_tool,
                                                   active_obj, pre_verts, 'X',
                                                   1.0)
                            self.lw_tool_axis = 'X'
                    else:
                        if self.lw_tool_axis:
                            if self.lw_tool_axis == 'Z':
                                l_widget.setup_lw_tool(rv3d, self.lw_tool,
                                                       active_obj, pre_verts,
                                                       'Z_Top', 1.0)
                                self.lw_tool_axis = 'Z_Top'
                            elif self.lw_tool_axis == 'Z_Top':
                                l_widget.setup_lw_tool(rv3d, self.lw_tool,
                                                       active_obj, pre_verts,
                                                       'Z_Bottom', 1.0)

                                ## revert direction
                                #stp = self.lw_tool.start_point.position.copy()
                                #self.lw_tool.start_point.position = self.lw_tool.end_point.position
                                #self.lw_tool.end_point.position = stp

                                self.lw_tool_axis = 'Z_Bottom'
                            elif self.lw_tool_axis == 'Z_Bottom':
                                l_widget.setup_lw_tool(rv3d, self.lw_tool,
                                                       active_obj, pre_verts,
                                                       'Z', 1.0)
                                self.lw_tool_axis = 'Z'
                            else:
                                l_widget.setup_lw_tool(rv3d, self.lw_tool,
                                                       active_obj, pre_verts,
                                                       'Z', 1.0)
                                self.lw_tool_axis = 'Z'
                        else:
                            l_widget.setup_lw_tool(rv3d, self.lw_tool,
                                                   active_obj, pre_verts, 'Z',
                                                   1.0)
                            self.lw_tool_axis = 'Z'

            elif event.type == 'I' and self.lw_tool:
                start_copy = self.lw_tool.start_point.position.copy()
                self.lw_tool.start_point.position = self.lw_tool.end_point.position
                self.lw_tool.end_point.position = start_copy

        # TOOL WORK!
        if self.tool_mode == 'MOVE_POINT':
            if event.value == 'RELEASE':
                self.tool_mode = 'IDLE'
                return {'RUNNING_MODAL'}
            else:
                # move points
                new_point_pos = ut_base.get_mouse_on_plane(
                    context, self.active_lw_point.position, None, m_coords)
                if self.active_lw_point.position == self.lw_tool.start_point.position or self.active_lw_point.position == self.lw_tool.end_point.position:
                    self.active_lw_point.position = new_point_pos
                    l_widget.update_middle_point(self.lw_tool)
                elif self.active_lw_point.position == self.lw_tool.middle_point.position:
                    self.lw_tool.start_point.position += new_point_pos - self.active_lw_point.position
                    self.lw_tool.end_point.position += new_point_pos - self.active_lw_point.position
                    self.lw_tool.middle_point.position = new_point_pos

                return {'RUNNING_MODAL'}

        elif self.tool_mode in {'SCALE_ALL', 'SCALE_FRONT', 'TAPE'}:
            if event.value == 'RELEASE' and event.type in {
                    'LEFTMOUSE', 'SELECTMOUSE'
            }:
                bm.normal_update()

                # add to undo history
                self.h_redo.clear()
                pre_work_verts = [bm.verts[v_id] for v_id in self.work_verts]
                add_history(pre_work_verts, self.h_undo)

                self.tool_mode = 'IDLE'

            elif self.do_update:
                # move points
                start_point_2d = view3d_utils.location_3d_to_region_2d(
                    region, rv3d, self.lw_tool.start_point.position)
                if start_point_2d:
                    tool_dist = (start_point_2d - self.deform_mouse_pos).length
                    now_dist = (start_point_2d - Vector(m_coords)).length
                    apply_value = (now_dist - tool_dist) / tool_dist
                    if apply_value != 0.0:
                        tool_orig = active_obj.matrix_world.inverted(
                        ) * self.lw_tool.start_point.position
                        tool_end = active_obj.matrix_world.inverted(
                        ) * self.lw_tool.end_point.position
                        tool_vec = tool_end - tool_orig
                        tool_dir = (tool_end - tool_orig).normalized()
                        for vert_data in self.apply_tool_verts:
                            scale_vec = None
                            scale_value = vert_data[1]

                            if self.tool_mode == 'SCALE_ALL':
                                scale_vec = (vert_data[2] - tool_orig)
                            elif self.tool_mode == 'SCALE_FRONT':
                                scale_vec = (tool_end - tool_orig)
                            else:
                                # TAPE
                                scale_vec = vert_data[2] - (
                                    tool_orig + (tool_dir * vert_data[1] *
                                                 (tool_vec).length))
                                scale_value = min(1.0, vert_data[1])

                            bm.verts[vert_data[0]].co = vert_data[2] + (
                                scale_vec * (scale_value * apply_value))

                        #bm.normal_update()
                        bmesh.update_edit_mesh(active_obj.data)

            self.do_update = False
            return {'RUNNING_MODAL'}

        elif self.tool_mode == 'MOVE_ALL':
            if event.value == 'RELEASE' and event.type in {
                    'LEFTMOUSE', 'SELECTMOUSE'
            }:
                bm.normal_update()

                # add to undo history
                self.h_redo.clear()
                pre_work_verts = [bm.verts[v_id] for v_id in self.work_verts]
                add_history(pre_work_verts, self.h_undo)

                self.tool_mode = 'IDLE'

            elif self.do_update:
                mouse_pos_3d = ut_base.get_mouse_on_plane(
                    context, self.lw_tool.start_point.position, None, m_coords)
                mouse_pos_3d = active_obj.matrix_world.inverted(
                ) * mouse_pos_3d
                start_pos = active_obj.matrix_world.inverted(
                ) * self.lw_tool.start_point.position
                orig_pos = active_obj.matrix_world.inverted(
                ) * self.deform_vec_pos
                orig_vec = orig_pos - start_pos
                move_vec = (mouse_pos_3d - start_pos) - orig_vec

                for vert_data in self.apply_tool_verts:
                    move_value = vert_data[1]
                    bm.verts[vert_data[0]].co = vert_data[2] + (move_vec *
                                                                move_value)

                #bm.normal_update()
                bmesh.update_edit_mesh(active_obj.data)
                self.do_update = False

            return {'RUNNING_MODAL'}

        elif self.tool_mode in {
                'ROTATE_ALL', 'TWIST', 'BEND_ALL', 'BEND_SPIRAL'
        }:
            if event.value == 'RELEASE' and event.type in {
                    'LEFTMOUSE', 'SELECTMOUSE'
            }:
                bm.normal_update()

                # add to undo history
                self.h_redo.clear()
                pre_work_verts = [bm.verts[v_id] for v_id in self.work_verts]
                add_history(pre_work_verts, self.h_undo)

                self.tool_mode = 'IDLE'

            elif self.do_update:
                m_coords = Vector(
                    m_coords)  # convert into vector for operations
                start_2d = view3d_utils.location_3d_to_region_2d(
                    region, rv3d, self.lw_tool.start_point.position)
                new_vec_dir = (m_coords - start_2d).normalized()
                rot_angle = new_vec_dir.angle(self.deform_vec_pos)

                start_3d = self.lw_tool.start_point.position
                end_3d = self.lw_tool.end_point.position

                if rot_angle != 0.0:
                    # check for left or right direction to rotate
                    vec_check_1 = Vector((new_vec_dir[0], new_vec_dir[1], 0))
                    vec_check_2 = Vector(
                        (new_vec_dir[0] - self.deform_vec_pos[0],
                         new_vec_dir[1] - self.deform_vec_pos[1], 0))
                    checker_side_dir = vec_check_1.cross(
                        vec_check_2).normalized()[2]
                    if checker_side_dir > 0.0:
                        rot_angle = -rot_angle

                    start_pos = self.lw_tool.start_point.position
                    rot_dir = None
                    if self.tool_mode == 'ROTATE_FRONT' or self.tool_mode == 'TWIST':
                        # ROTATE_FRONT code
                        rot_dir = (end_3d - start_3d).normalized()
                    else:
                        rot_dir = (rv3d.view_rotation * Vector(
                            (0.0, 0.0, -1.0))).normalized()

                    rot_angle += self.deform_mouse_pos  # add rot angle

                    bend_side_dir = None
                    faloff_len = None
                    spiral_value = 0.0  # only for BEND_SPIRAL
                    bend_scale_value = 1.0  # only for BEND_ALL

                    if self.tool_mode == 'BEND_ALL' or self.tool_mode == 'BEND_SPIRAL':
                        bend_side_dir = (((end_3d - start_3d).normalized()
                                          ).cross(rot_dir)).normalized()
                        faloff_len = end_3d - start_3d

                        if self.tool_mode == 'BEND_SPIRAL':
                            val_scale = None
                            if rot_angle > 0.0:
                                val_scale = (1.0 -
                                             ((m_coords - start_2d).length /
                                              self.bend_scale_len))
                            else:
                                val_scale = (((m_coords - start_2d).length /
                                              self.bend_scale_len))

                            spiral_value = 1.0 - (faloff_len.length *
                                                  val_scale)

                        else:
                            val_scale = (((m_coords - start_2d).length /
                                          self.bend_scale_len))
                            bend_scale_value = ((val_scale))

                    do_bend = False
                    if self.tool_mode == 'BEND_ALL' or self.tool_mode == 'BEND_SPIRAL':
                        do_bend = True

                    for vert_data in self.apply_tool_verts:
                        apply_value = vert_data[1]
                        final_apply_value = rot_angle * apply_value

                        # do rotation
                        if final_apply_value != 0.0:
                            rot_mat = Matrix.Rotation(final_apply_value, 3,
                                                      rot_dir)
                            vert = bm.verts[vert_data[0]]

                            if do_bend:
                                vert_temp = (active_obj.matrix_world *
                                             vert_data[2]) - (
                                                 (faloff_len) * apply_value)

                                back_offset = (
                                    ((faloff_len).length /
                                     (final_apply_value)) + spiral_value) * (
                                         apply_value * bend_scale_value)
                                vert_temp += bend_side_dir * back_offset
                                vert.co = vert_temp
                            else:
                                # set original position
                                vert.co[0] = vert_data[2][0]
                                vert.co[1] = vert_data[2][1]
                                vert.co[2] = vert_data[2][2]

                            # ROTATE VERTS!
                            if do_bend:
                                vert.co = rot_mat * (
                                    (vert.co) - start_pos) + start_pos
                                back_offset = (
                                    (faloff_len).length /
                                    (final_apply_value)) * (apply_value *
                                                            bend_scale_value)
                                vert.co = active_obj.matrix_world.inverted(
                                ) * (vert.co - (bend_side_dir * back_offset))
                            else:
                                vert.co = active_obj.matrix_world.inverted(
                                ) * (rot_mat *
                                     ((active_obj.matrix_world * vert.co) -
                                      start_pos) + start_pos)

                    self.deform_vec_pos = new_vec_dir
                    self.deform_mouse_pos = rot_angle  # set new angle rotation for next step

                    #bm.normal_update()
                    bmesh.update_edit_mesh(active_obj.data)
                self.do_update = False

            return {'RUNNING_MODAL'}

        else:
            if event.value == 'RELEASE' and event.type in {
                    'LEFTMOUSE', 'SELECTMOUSE'
            }:
                self.tool_mode = 'IDLE'
                return {'RUNNING_MODAL'}

        # main stuff
        if event.type in {'RIGHTMOUSE', 'ESC'}:
            context.space_data.show_manipulator = self.manipulator

            # bpy.types.SpaceView3D.draw_handler_remove(self.lin_deform_handle_3d, 'WINDOW')
            bpy.types.SpaceView3D.draw_handler_remove(
                self.lin_deform_handle_2d, 'WINDOW')

            context.area.header_text_set()

            return {'FINISHED'}

        elif event.type in self.pass_keys:
            # allow navigation
            return {'PASS_THROUGH'}

        return {'RUNNING_MODAL'}
def create_mesh(data_list, AFMdata, use_smooth, scale_size, scale_height,
                use_camera, use_lamp):
    # This is for the image name.
    path_list = AFMdata.datfile.strip('/').split('/')

    number_img = len(data_list)
    image_x_offset_gap = 10.0 * scale_size
    image_x_all = sum(AFMdata.x_size) * scale_size
    image_x_offset = -(image_x_all + image_x_offset_gap *
                       (number_img - 1)) / 2.0

    # For each image do:
    for k, data in enumerate(data_list):

        size_x = AFMdata.x_pixel[k]
        size_y = AFMdata.y_pixel[k]

        image_scale = AFMdata.x_size[k] / float(AFMdata.x_pixel[k])
        image_scale = image_scale * scale_size
        image_x_size = AFMdata.x_size[k] * scale_size
        image_x_offset += image_x_size / 2.0

        image_name = path_list[-1] + "_" + AFMdata.channel[k][1]

        data_mesh = []
        data_faces = []

        #print("passed - create_mesh ---- 1")

        for i, line in enumerate(data):
            for j, pixel in enumerate(line):

                # The vertices
                data_mesh.append(
                    Vector((float(i) * image_scale, float(j) * image_scale,
                            float(pixel) * scale_height)))

                # The faces
                if i < size_y - 1 and j < size_x - 1:
                    data_faces.append([
                        size_x * i + j, size_x * (i + 1) + j,
                        size_x * (i + 1) + j + 1, size_x * i + j + 1
                    ])

        #print("passed - create_mesh ---- 2")

        # Build the mesh
        surface_mesh = bpy.data.meshes.new("Mesh")
        surface_mesh.from_pydata(data_mesh, [], data_faces)
        surface_mesh.update()
        surface = bpy.data.objects.new(image_name, surface_mesh)
        bpy.context.scene.objects.link(surface)
        bpy.ops.object.select_all(action='DESELECT')
        surface.select = True

        bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
        # sum((v.co for v in mesh.vertices), Vector()) / len(mesh.vertices)

        if use_smooth:
            for polygon in surface.data.polygons:
                polygon.use_smooth = True

        surface.location = Vector((0.0, image_x_offset, 0.0))
        image_x_offset += image_x_size / 2.0 + image_x_offset_gap

        #print("passed - create_mesh ---- 3")

    object_center_vec = Vector((0.0, 0.0, 0.0))
    object_size = (sum(AFMdata.x_size) * scale_size + image_x_offset_gap *
                   (len(data_list) - 1))

    # ------------------------------------------------------------------------
    # CAMERA AND LAMP
    camera_factor = 20.0

    # If chosen a camera is put into the scene.
    if use_camera == True:

        # Assume that the object is put into the global origin. Then, the
        # camera is moved in x and z direction, not in y. The object has its
        # size at distance sqrt(object_size) from the origin. So, move the
        # camera by this distance times a factor of camera_factor in x and z.
        # Then add x, y and z of the origin of the object.
        object_camera_vec = Vector((sqrt(object_size) * camera_factor, 0.0,
                                    sqrt(object_size) * camera_factor))
        camera_xyz_vec = object_center_vec + object_camera_vec

        # Create the camera
        current_layers = bpy.context.scene.layers
        camera_data = bpy.data.cameras.new("A_camera")
        camera_data.lens = 45
        camera_data.clip_end = 50000.0
        camera = bpy.data.objects.new("A_camera", camera_data)
        camera.location = camera_xyz_vec
        camera.layers = current_layers
        bpy.context.scene.objects.link(camera)

        # Here the camera is rotated such it looks towards the center of
        # the object. The [0.0, 0.0, 1.0] vector along the z axis
        z_axis_vec = Vector((0.0, 0.0, 1.0))
        # The angle between the last two vectors
        angle = object_camera_vec.angle(z_axis_vec, 0)
        # The cross-product of z_axis_vec and object_camera_vec
        axis_vec = z_axis_vec.cross(object_camera_vec)
        # Rotate 'axis_vec' by 'angle' and convert this to euler parameters.
        # 4 is the size of the matrix.
        camera.rotation_euler = Matrix.Rotation(angle, 4, axis_vec).to_euler()

        # Rotate the camera around its axis by 90° such that we have a nice
        # camera position and view onto the object.
        bpy.ops.object.select_all(action='DESELECT')
        camera.select = True
        bpy.ops.transform.rotate(value=(90.0 * 2 * pi / 360.0),
                                 axis=object_camera_vec,
                                 constraint_axis=(False, False, False),
                                 constraint_orientation='GLOBAL',
                                 mirror=False,
                                 proportional='DISABLED',
                                 proportional_edit_falloff='SMOOTH',
                                 proportional_size=1,
                                 snap=False,
                                 snap_target='CLOSEST',
                                 snap_point=(0, 0, 0),
                                 snap_align=False,
                                 snap_normal=(0, 0, 0),
                                 release_confirm=False)

    # Here a lamp is put into the scene, if chosen.
    if use_lamp == True:

        # This is the distance from the object measured in terms of %
        # of the camera distance. It is set onto 50% (1/2) distance.
        lamp_dl = sqrt(object_size) * 15 * 0.5
        # This is a factor to which extend the lamp shall go to the right
        # (from the camera  point of view).
        lamp_dy_right = lamp_dl * (3.0 / 4.0)

        # Create x, y and z for the lamp.
        object_lamp_vec = Vector((lamp_dl, lamp_dy_right, lamp_dl))
        lamp_xyz_vec = object_center_vec + object_lamp_vec

        # Create the lamp
        current_layers = bpy.context.scene.layers
        lamp_data = bpy.data.lamps.new(name="A_lamp", type="POINT")
        lamp_data.distance = 5000.0
        lamp_data.energy = 3.0
        lamp_data.shadow_method = 'RAY_SHADOW'
        lamp = bpy.data.objects.new("A_lamp", lamp_data)
        lamp.location = lamp_xyz_vec
        lamp.layers = current_layers
        bpy.context.scene.objects.link(lamp)

        bpy.context.scene.world.light_settings.use_ambient_occlusion = True
        bpy.context.scene.world.light_settings.ao_factor = 0.1
Example #38
0
def create_poly_box(polyline, allverts, materials, geocenter):

    materialindex = int(polyline.attrib["m"])
    idxs = []
    idxs.append(int(polyline.attrib["v1"]))
    idxs.append(int(polyline.attrib["v2"]))
    idxs.append(int(polyline.attrib["v3"]))
    idxs.append(int(polyline.attrib["v4"]))
    verts = []
    for idx in idxs:
        verts.append(allverts[idx])

    p1 = verts[0]
    p2 = verts[1]
    p3 = verts[2]
    p4 = verts[3]

    a, b, c, d = p1, p2, p3, p4

    cf1 = (a + b) / 2
    cf2 = (c + d) / 2
    cf3 = (a + d) / 2
    cf4 = (b + c) / 2
    cf5 = (a + c) / 2
    cf6 = (b + d) / 2

    # caclulate box center
    center = (cf3 + cf4) / 2

    rightest = get_closest_axis_point(Vector((1, 0, 0)), center,
                                      [cf1, cf2, cf3, cf4, cf5, cf6])
    upest = get_closest_axis_point(Vector((0, 0, 1)), center,
                                   [cf1, cf2, cf3, cf4, cf5, cf6])
    right = (rightest - center).normalized()
    up = (upest - center).normalized()
    forward = Vector.cross(right, up).normalized()

    mat = Matrix.Identity(4)

    mat[0] = (right.x, right.y, right.z, 0)
    mat[1] = (up.x, up.y, up.z, 0)
    mat[2] = (forward.x, forward.y, forward.z, 0)
    mat[3] = (0, 0, 0, 1)

    mat.normalize()

    rotation = mat.to_quaternion().inverted().normalized().to_euler('XYZ')

    # calculate scale
    seq = [cf1, cf2, cf3, cf4, cf5, cf6]

    _cf1 = get_closest_axis_point(right, center, seq)
    _cf2 = get_closest_axis_point(-right, center, seq)
    _cf3 = get_closest_axis_point(-up, center, seq)
    _cf4 = get_closest_axis_point(up, center, seq)
    _cf5 = get_closest_axis_point(-forward, center, seq)
    _cf6 = get_closest_axis_point(forward, center, seq)

    W = (_cf2 - _cf1).length
    L = (_cf3 - _cf4).length
    H = (_cf5 - _cf6).length

    scale = Vector((W, L, H))

    # blender stuff
    mesh = bpy.data.meshes.new("box")
    bm = bmesh.new()

    bmesh.ops.create_cube(bm, size=1)
    bm.to_mesh(mesh)
    bm.free()

    mat = materials[materialindex]
    mesh.materials.append(mat)

    obj = bpy.data.objects.new("box", mesh)

    #obj.data.materials.append(material)

    #location = center + geocenter

    obj.location = center  #location #boxes dont use geocenter to offset?
    obj.rotation_euler = rotation
    obj.scale = scale
    obj.sollumtype = "Bound Box"

    return obj
Example #39
0
class GlHandle(GlPolygon):

    def __init__(self, sensor_size, size, draggable=False, selectable=False, d=3):
        """
            sensor_size : 2d size in pixels of sensor area
            size : 3d size of handle
        """
        GlPolygon.__init__(self, d=d)
        self.colour_active = (1.0, 0.0, 0.0, 1.0)
        self.colour_hover = (1.0, 1.0, 0.0, 1.0)
        self.colour_normal = (1.0, 1.0, 1.0, 1.0)
        self.colour_selected = (0.0, 0.0, 0.7, 1.0)
        self.size = size
        self.sensor_width = sensor_size
        self.sensor_height = sensor_size
        self.pos_3d = Vector((0, 0, 0))
        self.up_axis = Vector((0, 0, 0))
        self.c_axis = Vector((0, 0, 0))
        self.hover = False
        self.active = False
        self.draggable = draggable
        self.selectable = selectable
        self.selected = False

    def set_pos(self, context, pos_3d, direction, normal=Vector((0, 0, 1))):
        self.up_axis = direction.normalized()
        self.c_axis = self.up_axis.cross(normal)
        self.pos_3d = pos_3d
        self.pos_2d = self.position_2d_from_coord(context, self.sensor_center)

    def check_hover(self, pos_2d):
        if self.draggable:
            dp = pos_2d - self.pos_2d
            self.hover = abs(dp.x) < self.sensor_width and abs(dp.y) < self.sensor_height

    @property
    def sensor_center(self):
        pts = self.pts
        n = len(pts)
        x, y, z = 0, 0, 0
        for pt in pts:
            x += pt.x
            y += pt.y
            z += pt.z
        return Vector((x / n, y / n, z / n))

    @property
    def pts(self):
        raise NotImplementedError

    @property
    def colour(self):
        if self.render:
            return self.colour_inactive
        elif self.draggable:
            if self.active:
                return self.colour_active
            elif self.hover:
                return self.colour_hover
            elif self.selected:
                return self.colour_selected
            return self.colour_normal
        else:
            return self.colour_inactive
Example #40
0
def add_f_curve_modifiers(armature_object, strength, speed):
    wind_vector = Vector((1, 0, 0)) * strength

    fcurves = armature_object.animation_data.action.fcurves
    for f in fcurves:
        for m in f.modifiers:
            f.modifiers.remove(m)

    bones = organize_bones(armature_object)

    for b in bones:
        mass = b.bone.tail_radius**2 * b.length
        barycenter = b.tail * mass
        for c in b.children:
            mass += c["mass"]
            barycenter += Vector(c["barycenter"])
        b["mass"] = mass
        b["barycenter"] = barycenter
        barycenter /= mass

        b.rotation_mode = 'XYZ'
        b.keyframe_insert('rotation_euler', frame=0, index=0)
        b.keyframe_insert('rotation_euler', frame=0, index=2)

    fcurves = armature_object.animation_data.action.fcurves

    for i in range(len(bones)):
        f0 = fcurves[2 * i]
        f1 = fcurves[2 * i + 1]
        b = bones[i]

        i_base = b.matrix.to_3x3().inverted()
        bone_vector = b.tail - b.head

        inertia_moment = bone_vector.length**2 * bones[i]["mass"] / 10000
        damping = 0.5 * b.bone.tail_radius
        stiffness = b.bone.tail_radius**2 / b.length * 800
        if b.parent is not None and len(b.parent.children) > 1:
            stiffness *= 2
            # torque /= 3
        # else:
        #     torque = Vector((0, 0, 0))
        torque = i_base * wind_vector.cross(bone_vector) / (
            b.bone.tail_radius) / 1000

        f = sqrt(abs(damping**2 - 4 * inertia_moment * stiffness)) / (
            5 * b.bone.tail_radius) * speed

        x_amplitude = torque.x
        z_amplitude = torque.z

        m0 = f0.modifiers.new(type='FNGENERATOR')
        m1 = f1.modifiers.new(type='FNGENERATOR')
        m0.function_type = 'SIN'
        m1.function_type = 'SIN'
        m0.amplitude = x_amplitude
        m1.amplitude = z_amplitude
        m0.phase_multiplier = f
        m1.phase_multiplier = f
        m0.value_offset = x_amplitude * 3
        m1.value_offset = z_amplitude * 3
Example #41
0
    def execute(self, context):

        if self.reset_values is True:
            self.reset_all_values()

        active_obj = context.scene.objects.active

        bm = bmesh.from_edit_mesh(active_obj.data)
        bm.verts.ensure_lookup_table()
        #verts = [v for v in bm.verts if v.select]

        # get loops
        loops = loop_t.get_connected_input(bm)
        loops = loop_t.check_loops(loops, bm)

        if not loops:
            self.report({'WARNING'}, "No Loops!")
            return {'CANCELLED'}

        first_indexes = []
        if isinstance(bm.select_history[0], bmesh.types.BMVert):
            for element in bm.select_history:
                first_indexes.append(element.index)
        elif isinstance(bm.select_history[0], bmesh.types.BMEdge):
            for element in bm.select_history:
                el_verts = element.verts
                first_indexes.append(el_verts[0].index)
                first_indexes.append(el_verts[1].index)

        for loop in loops:
            if loop[1] is True:
                continue

            loop_verts = []

            for ind in loop[0]:
                loop_verts.append(bm.verts[ind])

            #  for the case if we need to reverse it
            if loop[0][-1] in first_indexes:
                loop_verts = list(reversed(loop_verts))

            # reverse again for the direction
            if self.reverse_direction is True:
                loop_verts = list(reversed(loop_verts))

            # positions
            first_vert_pos = active_obj.matrix_world * loop_verts[0].co
            last_vert_pos = active_obj.matrix_world * loop_verts[-1].co

            loop_centr_orig = first_vert_pos.lerp(last_vert_pos, 0.5)
            relative_dist = (first_vert_pos - loop_centr_orig).length
            sidevec = (first_vert_pos - last_vert_pos).normalized()

            obj_matrix = active_obj.matrix_world
            obj_matrix_inv = obj_matrix.inverted()

            if self.direction_vector == 'Custom':
                rot_dir = Vector((self.rotate_axis[0], self.rotate_axis[1],
                                  self.rotate_axis[2])).normalized()
            elif self.direction_vector == 'MiddleCrossed':
                middle_nor = loop_verts[int(len(loop_verts) /
                                            2)].normal.copy().normalized()
                middle_nor = ut_base.get_normal_world(middle_nor, obj_matrix,
                                                      obj_matrix_inv)
                rot_dir = middle_nor.cross(sidevec).normalized()

                # fix only for MiddleCrossed
                if not self.reverse_direction:
                    rot_dir.negate()

            else:
                middle_nor = loop_verts[int(len(loop_verts) /
                                            2)].normal.copy().normalized()
                middle_nor = ut_base.get_normal_world(middle_nor, obj_matrix,
                                                      obj_matrix_inv)
                middle_nor = middle_nor.cross(sidevec).normalized()
                rot_dir = middle_nor.cross(sidevec).normalized()
                rot_dir.negate()

            upvec = rot_dir.cross(sidevec).normalized()
            loop_centr = (self.upvec_offset * upvec *
                          relative_dist) + loop_centr_orig

            loop_angle = (first_vert_pos - loop_centr).normalized().angle(
                (last_vert_pos - loop_centr).normalized())
            if self.upvec_offset > 0:
                loop_angle = math.radians((360 - math.degrees(loop_angle)))

            # even spread
            line_data = None
            if self.spread_mode == 'Even':
                world_verts = [
                    active_obj.matrix_world * vert.co for vert in loop_verts
                ]

                line_data = []
                line_length = 0.0
                for i, vec in enumerate(world_verts):
                    if i == 0:
                        line_data.append(0)
                    else:
                        line_length += (vec - world_verts[i - 1]).length
                        line_data.append(line_length)

            # make arc!
            for i, vert in enumerate(loop_verts):
                if i != 0 and i != len(loop_verts) - 1:
                    if self.spread_mode == 'Normal':
                        rot_angle = loop_angle * (i / (len(loop_verts) - 1))
                    else:
                        rot_angle = loop_angle * (
                            line_data[i] / line_data[len(loop_verts) - 1])

                    rot_mat = Matrix.Rotation(rot_angle, 3, rot_dir)
                    vert_pos = (rot_mat *
                                (first_vert_pos - loop_centr)) + loop_centr

                    if self.scale_arc != 0:
                        vert_rel_dist = mathu.geometry.distance_point_to_plane(
                            vert_pos, loop_centr_orig, upvec)
                        vert_rel_dist_max = self.upvec_offset + relative_dist

                        if vert_rel_dist != 0 and vert_rel_dist_max != 0:
                            vert_pos_offset = vert_rel_dist / vert_rel_dist_max
                            vert_pos += (self.scale_arc * upvec *
                                         vert_pos_offset * vert_rel_dist_max)

                    # rotate arc
                    if self.rotate_arc_axis != 0:
                        rot_mat_2 = Matrix.Rotation(
                            math.radians(self.rotate_arc_axis), 3, sidevec)
                        vert_pos = (
                            rot_mat_2 *
                            (vert_pos - loop_centr_orig)) + loop_centr_orig

                    vert.co = active_obj.matrix_world.inverted() * vert_pos

        bm.normal_update()
        bmesh.update_edit_mesh(active_obj.data)

        return {'FINISHED'}
Example #42
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')
        # print(bpy.data.materials['bond'])
        bpy.data.materials[
            'bond'].diffuse_color = atom_data['bond']['color'] + [1.0]
        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']

    # draw atoms
    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'] + [
                1.0
            ]
            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.collection.objects.link(atom_sphere)
        shapes.append(atom_sphere)

    # draw bonds
    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.collection.objects.link(bond_cylinder)
            shapes.append(bond_cylinder)

    # Remove primitive meshes
    bpy.ops.object.select_all(action='DESELECT')
    sphere.select_set(state=True)
    if show_bonds:
        cylinder.select_set(state=True)

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

    for shape in shapes:
        shape.select_set(state=True)
    bpy.context.view_layer.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.view_layer.update()
def lookFrom(p):
    y = Vector([0,0,1])
    z = p
    x = y.cross(z).normalized()
    y = z.cross(x).normalized()
    return Matrix.Translation(p) @ Matrix([x, y, z]).transposed().to_4x4()
    def execute(self, context):

        self.hcol = context.preferences.addons[
            'kekit'].preferences.modal_color_header
        self.tcol = context.preferences.addons[
            'kekit'].preferences.modal_color_text
        self.scol = context.preferences.addons[
            'kekit'].preferences.modal_color_subtext

        if self.ke_fitprim_option == "BOX":
            self.boxmode, self.world = True, False
        elif self.ke_fitprim_option == "CYL":
            self.boxmode, self.world = False, False
        elif self.ke_fitprim_option == "SPHERE":
            self.boxmode, self.world, self.sphere = False, False, True
        elif self.ke_fitprim_option == "QUADSPHERE":
            self.boxmode, self.world, self.sphere = False, False, True
        else:
            self.boxmode, self.world, self.sphere = False, False, False

        if bpy.context.scene.kekit.fitprim_item:
            self.itemize = True
        else:
            self.itemize = self.ke_fitprim_itemize

        self.modal = bpy.context.scene.kekit.fitprim_modal
        self.cyl_sides = bpy.context.scene.kekit.fitprim_sides
        self.select = bpy.context.scene.kekit.fitprim_select
        self.unit = round(bpy.context.scene.kekit.fitprim_unit, 4)
        self.sphere_ring = bpy.context.scene.kekit.fitprim_sphere_ring
        self.sphere_seg = bpy.context.scene.kekit.fitprim_sphere_seg
        self.quadsphere_seg = bpy.context.scene.kekit.fitprim_quadsphere_seg

        self.edit_mode = bpy.context.mode
        sel_verts = []
        sel_verts2 = []
        multi_object_mode = False

        cursor = context.scene.cursor
        self.og_cloc = cursor.location.copy()
        self.og_crot = cursor.rotation_euler.copy()
        og_orientation = str(context.scene.transform_orientation_slots[0].type)

        if self.itemize or self.edit_mode == "OBJECT" and context.object is not None:
            # make sure the new object is in the same layer as context object
            objc = context.object.users_collection[0]
            layer_collection = context.view_layer.layer_collection
            layer_coll = get_layer_collection(layer_collection, objc.name)
            alc = context.view_layer.active_layer_collection

            if objc.name != alc.name and alc.name != "Master Collection":
                context.view_layer.active_layer_collection = layer_coll

            elif objc.name != "Master Collection" and objc.name != alc.name:
                context.view_layer.active_layer_collection = layer_coll

        # -----------------------------------------------------------------------------------------
        # MULTI OBJECT CHECK & SETUP
        # -----------------------------------------------------------------------------------------
        if self.edit_mode == "EDIT_MESH":
            sel_mode = [b for b in bpy.context.tool_settings.mesh_select_mode]
            other_side = []
            sel_obj = [
                o for o in bpy.context.selected_objects if o.type == "MESH"
            ]
            if len(sel_obj) == 2:
                multi_object_mode = True

            obj = bpy.context.active_object
            obj_mtx = obj.matrix_world.copy()
            second_obj = []

            bm = bmesh.from_edit_mesh(obj.data)
            bm.faces.ensure_lookup_table()
            bm.verts.ensure_lookup_table()

            sel_verts = [v for v in bm.verts if v.select]

            if sel_mode[2]:
                sel_poly = [p for p in bm.faces if p.select]
                active_face = bm.faces.active
                if active_face not in sel_poly:
                    active_face = None

            if multi_object_mode:
                second_obj = [o for o in sel_obj if o != obj][0]
                bm2 = bmesh.from_edit_mesh(second_obj.data)
                obj_mtx2 = second_obj.matrix_world.copy()

                sel_poly2 = [p for p in bm2.faces if p.select]
                if sel_poly2:
                    active_face2 = bm2.faces.active
                    if active_face2 not in sel_poly2:
                        active_face2 = None
                    if active_face2:
                        sel_verts2 = active_face2.verts
                    else:
                        sel_verts2 = sel_poly2[0].verts
                        active_face2 = sel_poly2[0]  # haxxfixxx
                else:
                    sel_verts2 = [v for v in bm2.verts if v.select]

                if not sel_verts2:
                    multi_object_mode = False

                elif sel_mode[0]:
                    # Just for 2-vert mode in multiobj mode
                    ole = sel_verts2[0].link_edges[:]
                    other_side = get_shortest(obj_mtx2, ole)

            side = None
            distance = 0
            vps = []
            normal, setpos, setrot, center = None, None, None, None
            first_island = None
            island_mode = False

        # -----------------------------------------------------------------------------------------
        # NO SELECTION MODE
        # -----------------------------------------------------------------------------------------
        if not sel_verts or self.edit_mode == "OBJECT":

            # Check mouse over target
            if not self.edit_mode == "OBJECT":
                bpy.ops.object.mode_set(mode="OBJECT")

            hit_obj, hit_wloc, hit_normal, hit_face = mouse_raycast(
                context, self.mouse_pos, evaluated=True)

            if not self.edit_mode == "OBJECT":
                bpy.ops.object.mode_set(mode="EDIT")

            if self.edit_mode != "OBJECT" and hit_obj is not None:
                if len(hit_obj.modifiers) > 0:
                    self.report({
                        "INFO"
                    }, "FitPrim: Selected elements required in Edit Mode if Modifiers exist"
                                )
                    return {"CANCELLED"}

            if hit_obj and hit_face is not None:
                # mouse over placement on face
                self.world = False
                mtx = hit_obj.matrix_world
                eks = hit_obj.data.polygons[hit_face].edge_keys
                vecs = []

                for vp in eks:
                    vc1 = mtx @ hit_obj.data.vertices[vp[0]].co
                    vc2 = mtx @ hit_obj.data.vertices[vp[1]].co
                    vecs.append(Vector(vc1 - vc2).normalized())

                evps = []
                for vp in eks:
                    v1, v2 = hit_obj.data.vertices[
                        vp[0]], hit_obj.data.vertices[vp[1]]
                    evps.append([v1, v2])

                side, center, start_vec = get_sides(mtx, vecs, evps)

                if self.ke_fitprim_option == "PLANE" and self.edit_mode == "OBJECT":
                    setpos = center
                    # setpos = mtx @ hit_obj.data.polygons[hit_face].center
                    side *= 2

                else:
                    offset = hit_normal * (side / 2)
                    setpos = center + offset
                    # setpos = mtx @ hit_obj.data.polygons[hit_face].center + offset

                setrot = rotation_from_vector(hit_normal, start_vec)

            else:
                # view placement compensation & Z0 drop
                view_vec = region_2d_to_vector_3d(context.region,
                                                  context.space_data.region_3d,
                                                  self.mouse_pos)
                view_pos = context.space_data.region_3d.view_matrix.inverted(
                ).translation

                if get_view_type() != "ORTHO":
                    ground = ((0, 0, 0), (0, 1, 0), (1, 0, 0))
                    raypos = intersect_ray_tri(ground[0], ground[1], ground[2],
                                               view_vec, view_pos, False)
                    snap_val = str(self.unit).split('.')
                    if int(snap_val[0]) > 0:
                        snap = 0
                    else:
                        snap = len(snap_val[1])

                    setpos = Vector((round(raypos[0],
                                           snap), round(raypos[1],
                                                        snap), self.unit / 2))
                else:
                    setpos = region_2d_to_location_3d(
                        context.region, context.space_data.region_3d,
                        self.mouse_pos, view_vec)

                self.world = True

                side = self.unit
                if self.ke_fitprim_option == "PLANE" and self.edit_mode == "OBJECT":
                    side *= 2

            distance = side
            sel_mode = (True, False, False)

        # -----------------------------------------------------------------------------------------
        # VERT MODE(s)
        # -----------------------------------------------------------------------------------------
        elif sel_mode[0]:

            if len(sel_verts) == 1 and not multi_object_mode:
                # 1-VERT MODE
                self.world = True
                side = get_shortest(obj_mtx, sel_verts[0].link_edges[:])
                distance = side
                setpos = obj_mtx @ sel_verts[0].co

            elif len(sel_verts) == 2 and not multi_object_mode or \
                    multi_object_mode and len(sel_verts2) == 1 and len(sel_verts) == 1:
                # 2 Vert mode
                if multi_object_mode:
                    p1 = obj_mtx @ sel_verts[0].co
                    p2 = obj_mtx2 @ sel_verts2[0].co
                    side = [other_side]
                    con_edges = sel_verts[0].link_edges[:]
                    side.append(get_shortest(obj_mtx, con_edges))
                    side = sorted(side)[0]
                else:
                    p1 = obj_mtx @ sel_verts[0].co
                    p2 = obj_mtx @ sel_verts[1].co
                    con_edges = sel_verts[0].link_edges[:] + sel_verts[
                        1].link_edges[:]
                    side = get_shortest(obj_mtx, con_edges)

                v_1 = Vector(p1 - p2).normalized()
                v_2 = Vector((0, 0, 1))
                if abs(v_1.dot(v_2)) == 1:
                    v_2 = Vector((1, 0, 0))
                n_v = v_1.cross(v_2).normalized()
                u_v = n_v.cross(v_1).normalized()
                t_v = u_v.cross(n_v).normalized()

                setrot = Matrix((u_v, n_v, t_v)).to_4x4().inverted()
                distance = get_distance(p1, p2)
                setpos = Vector(get_midpoint(p1, p2))

            elif len(sel_verts) == 4 or multi_object_mode and len(
                    sel_verts2) + len(sel_verts) <= 4:
                # 4-vert Rectangle mode
                # holy brute force batman
                if not second_obj:
                    second_obj = obj
                tri = tri_order(((obj, sel_verts), (second_obj, sel_verts2)))
                q = tri[-1]
                # just placing a unit prim with min side in the center (for this one) so disregarding other vecs
                v1 = Vector(tri[0][0].matrix_world @ tri[0][1].co -
                            tri[1][0].matrix_world @ tri[1][1].co)
                v2 = Vector(tri[0][0].matrix_world @ tri[0][1].co -
                            tri[2][0].matrix_world @ tri[2][1].co)
                # v3 = Vector(q[0].matrix_world @ q[1].co - tri[1][0].matrix_world @ tri[1][1].co)
                # v4 = Vector(q[0].matrix_world @ q[1].co - tri[2][0].matrix_world @ tri[2][1].co)

                d1 = get_distance(tri[0][0].matrix_world @ tri[0][1].co,
                                  tri[1][0].matrix_world @ tri[1][1].co)
                # d2 = get_distance(tri[0][0].matrix_world @ tri[0][1].co, tri[2][0].matrix_world @ tri[2][1].co)
                # d3 = get_distance(q[0].matrix_world @ q[1].co, tri[1][0].matrix_world @ tri[1][1].co)
                d4 = get_distance(q[0].matrix_world @ q[1].co,
                                  tri[2][0].matrix_world @ tri[2][1].co)

                if d1 < d4:
                    side = d1
                else:
                    side = d4
                distance = side

                ap1 = average_vector([obj_mtx @ v.co for v in sel_verts])
                if sel_verts2:
                    ap2 = average_vector([obj_mtx2 @ v.co for v in sel_verts2])
                    setpos = average_vector((ap1, ap2))
                else:
                    setpos = ap1

                n = v1.normalized().cross(v2.normalized())
                u = n.cross(v1.normalized())
                t = u.cross(n)
                setrot = Matrix((u, n, t)).to_4x4().inverted()

            else:
                self.report({
                    "INFO"
                }, "FitPrim: Invalid Vert Mode Selection: Select 1, 2 or 4 verts"
                            )
                return {"CANCELLED"}

        # -----------------------------------------------------------------------------------------
        # EDGE MODE
        # -----------------------------------------------------------------------------------------
        elif sel_mode[1]:

            one_line, loops, loops2 = False, [], []
            sel_edges = [e for e in bm.edges if e.select]
            vps = [e.verts[:] for e in sel_edges]

            active_edge = bm.select_history.active
            if active_edge:
                active_edge_facenormals = [
                    correct_normal(obj_mtx, p.normal)
                    for p in active_edge.link_faces
                ]
                active_edge_normal = Vector(
                    average_vector(active_edge_facenormals)).normalized()

            # GET ISLANDS
            if multi_object_mode and active_edge:
                # print("multi obj mode") # todo: limited multiobj mode, rework one-for-all?
                sel_edges2 = [e for e in bm2.edges if e.select]
                vps2 = [e.verts for e in sel_edges2]
                p1 = get_loops(vps, legacy=True)
                p2 = get_loops(vps2, legacy=True)
                if p1 and p2:
                    a1, a2 = obj_mtx @ p1[0][0].co, obj_mtx @ p1[0][-1].co
                    b1, b2 = obj_mtx2 @ p2[0][0].co, obj_mtx2 @ p2[0][-1].co
                else:
                    a1, a2 = obj_mtx @ sel_verts[0].co, obj_mtx @ sel_verts[
                        -1].co
                    b1, b2 = obj_mtx2 @ sel_verts2[
                        0].co, obj_mtx2 @ sel_verts2[-1].co

                b_avg = get_midpoint(b1, b2)
                spacing, mp = get_closest_midpoint(a1, a2, b_avg)

                u_v = Vector(a1 - b1).normalized()
                t_v = Vector(a1 - a2).normalized()
                n_v = u_v.cross(t_v).normalized()

                setrot = rotation_from_vector(n_v, t_v, rotate90=True)
                setpos = mp
                side = spacing
                distance = spacing

            elif active_edge:  # same (active) obj island selections
                if len(sel_edges) > 1:

                    if len(sel_edges) == 2 and not bool(
                            set(sel_edges[0].verts).intersection(
                                sel_edges[1].verts)):
                        a1p, a2p = sel_edges[0].verts, sel_edges[1].verts
                        a1, a2 = obj_mtx @ a1p[0].co, obj_mtx @ a1p[1].co
                        b_avg = average_vector(
                            [obj_mtx @ a2p[0].co, obj_mtx @ a2p[1].co])

                        lf1 = sel_edges[0].link_faces[:]
                        lf = [
                            f for f in sel_edges[1].link_faces[:] if f in lf1
                        ]

                        v1 = Vector((obj_mtx @ a1p[0].co -
                                     obj_mtx @ a1p[1].co)).normalized()
                        v2 = Vector((obj_mtx @ a2p[0].co -
                                     obj_mtx @ a2p[1].co)).normalized()

                        if not lf:
                            u_v = Vector(a1 -
                                         (obj_mtx @ a2p[0].co)).normalized()
                            t_v = Vector(a1 - a2).normalized()
                            n_v = u_v.cross(t_v).normalized()
                            setrot = rotation_from_vector(n_v,
                                                          t_v,
                                                          rotate90=True)
                        else:
                            n = correct_normal(obj_mtx, lf[0].normal)
                            # t = average_vector((v1, v2))
                            # if sum(t) == 0:
                            #     print("AVG FAIL")
                            t = v1
                            setrot = rotation_from_vector(n, t, rotate90=True)

                        spacing, mp = get_closest_midpoint(a1, a2, b_avg)
                        setpos = mp
                        side = spacing
                        distance = spacing
                    else:
                        loops = get_loops(vps, legacy=True)

                if len(loops) > 1:
                    # print ("single obj - multi loop", len(loops))
                    # Check for closed loops
                    a_ep1, a_ep2 = loops[0][0], loops[0][-1]
                    b_ep1, b_ep2 = loops[1][0], loops[1][-1]
                    if a_ep1 == a_ep2:
                        a_ep2 = loops[0][-2]
                    if b_ep1 == b_ep2:
                        b_ep2 = loops[1][-2]

                    # get coords & set vals
                    a1, a2 = obj_mtx @ a_ep1.co, obj_mtx @ a_ep2.co
                    b1, b2 = obj_mtx @ b_ep1.co, obj_mtx @ b_ep2.co

                    b_avg = get_midpoint(b1, b2)
                    spacing, mp = get_closest_midpoint(a1, a2, b_avg)

                    u_v = Vector((0, 0, 1))
                    t_v = Vector(a1 - a2).normalized()
                    if abs(u_v.dot(t_v)) == 1:
                        u_v = Vector((1, 0, 0))

                    n_v = u_v.cross(t_v).normalized()

                    setrot = rotation_from_vector(n_v, t_v, rotate90=False)
                    setpos = mp
                    side = spacing
                    distance = spacing

                elif len(loops) == 1 or len(sel_edges) == 1:
                    # print ("single obj - single loop")
                    single_line = False
                    if len(sel_edges) != 1:
                        if loops[0][0] != loops[0][-1]:
                            single_line = True

                    if len(sel_edges) != 1 and not single_line:
                        vecs = [
                            Vector((obj_mtx @ vp[0].co) -
                                   (obj_mtx @ vp[1].co)).normalized()
                            for vp in vps
                        ]
                        normal = average_vector([
                            correct_normal(obj_mtx, v.normal)
                            for v in flatten(vps)
                        ])

                        side, center, start_vec = get_sides(obj_mtx, vecs, vps)
                        distance = side
                        setpos = center
                        setrot = rotation_from_vector(normal, start_vec)

                    elif len(sel_edges) == 1 or single_line:
                        # print("1 edge --> one line", one_line)
                        p1, p2 = obj_mtx @ sel_verts[
                            0].co, obj_mtx @ sel_verts[1].co
                        t_v = Vector(p1 - p2).normalized()
                        n_v = active_edge_normal

                        setrot = rotation_from_vector(n_v, t_v)
                        distance = get_distance(p1, p2)
                        setpos = get_midpoint(p1, p2)
                        side = distance
                    else:
                        print("Unexpected: Aborting operation.")
                        return {'CANCELLED'}

        # -----------------------------------------------------------------------------------------
        # FACE MODE
        # -----------------------------------------------------------------------------------------
        elif sel_mode[2] and active_face and sel_poly:
            fail_island = False

            # GET ISLANDS
            if multi_object_mode and active_face:
                first_island = sel_verts
                point = obj_mtx @ active_face.calc_center_median()
                firstcos = [obj_mtx @ v.co for v in active_face.verts]
                secondcos = [obj_mtx2 @ v.co for v in active_face2.verts]

                if firstcos and secondcos:
                    island_mode = True

                    distance = point_to_plane(point, firstcos, secondcos)
                    if distance:
                        distance = abs(distance)
                    else:
                        # print("Multi obj Point to plane failed for some reason - using single island mode.")
                        # island_mode = False
                        fail_island = True

            else:  # same (active) obj island selections
                first_island, second_island = get_selection_islands(
                    sel_poly, active_face)
                # Ofc, needs correct order for point to plane, and I'll just rely on face.verts for that:
                calc_island_1 = active_face.verts[:]
                calc_island_2 = []
                for p in sel_poly:
                    verts = p.verts[:]
                    for v in verts:
                        if v in second_island:
                            calc_island_2 = verts
                            break

                if len(first_island) != 0 and len(second_island) != 0:
                    firstcos = [obj_mtx @ v.co for v in calc_island_1]
                    secondcos = [obj_mtx @ v.co for v in calc_island_2]
                    distance = point_to_plane(
                        obj_mtx @ active_face.calc_center_median(), firstcos,
                        secondcos)

                    if distance:
                        distance = abs(distance)
                        island_mode = True
                    else:
                        # print("Point to plane failed for some reason - using single island mode.")
                        fail_island = True
                    # print(distance)
                else:
                    # Ngon mode
                    first_island = sel_verts

            # GETVALUES
            if island_mode or fail_island:
                bpy.ops.mesh.select_all(action='DESELECT')

                for v in first_island:
                    bm.verts[v.index].select = True

                bmesh.update_edit_mesh(obj.data)
                bpy.ops.mesh.select_mode(type='VERT')
                bpy.ops.mesh.select_mode(type='FACE')
                bm.faces.ensure_lookup_table()

            faces = [f for f in bm.faces if f.select]
            normal = average_vector(
                [correct_normal(obj_mtx, f.normal) for f in faces])
            bpy.ops.mesh.region_to_loop()

            sel_edges = [e for e in bm.edges if e.select]
            vps = [e.verts[:] for e in sel_edges]
            vecs = [
                Vector((obj_mtx @ vp[0].co) -
                       (obj_mtx @ vp[1].co)).normalized() for vp in vps
            ]

            side, center, start_vec = get_sides(obj_mtx, vecs, vps)
            setrot = rotation_from_vector(normal, start_vec)

        # -----------------------------------------------------------------------------------------
        # PROCESS
        # -----------------------------------------------------------------------------------------
        if side:
            if self.ke_fitprim_option == "PLANE" and self.edit_mode != "OBJECT":
                if sel_mode[2]:
                    setpos = center

            elif len(sel_verts) == 0 or sel_mode[0] or sel_mode[1]:
                side *= .5
                distance = distance / 2
                if self.sphere and sel_mode[0]:
                    if not len(sel_verts) == 0:
                        side = distance

            elif sel_mode[2]:
                if island_mode:
                    side *= .5
                    offset = normal * distance * .5
                    distance *= .5
                    if self.sphere:
                        side = distance
                else:
                    side *= .5
                    distance = side
                    offset = normal * side

                setpos = center + offset
                if not island_mode and self.sphere:
                    setpos = setpos - offset

            # SET FINAL ROTATION
            if self.world:
                setrot = (0, 0, 0)
                if self.ke_fitprim_option == "PLANE" and not sel_verts:
                    setpos[2] = 0
            else:
                setrot = setrot.to_euler()

            # RUN OP
            if not self.edit_mode == "OBJECT":
                bpy.ops.mesh.select_mode(type='FACE')

            if self.itemize:
                if self.edit_mode != "OBJECT":
                    bpy.ops.object.mode_set(mode="OBJECT")
                bpy.ops.transform.select_orientation(orientation='GLOBAL')
                cursor.location = setpos
                cursor.rotation_euler = setrot

            # -----------------------------------------------------------------------------------------
            # CUBE
            # -----------------------------------------------------------------------------------------
            if self.boxmode:
                bpy.ops.mesh.primitive_box_add(width=side,
                                               depth=side,
                                               height=distance,
                                               align='WORLD',
                                               location=setpos,
                                               rotation=setrot)
                if self.itemize or self.edit_mode == "OBJECT":
                    bpy.ops.object.shade_smooth()
                    bpy.context.object.data.use_auto_smooth = True

            # -----------------------------------------------------------------------------------------
            # PLANE
            # -----------------------------------------------------------------------------------------
            elif self.ke_fitprim_option == "PLANE":

                # side *= 2
                enter = True

                if self.edit_mode == 'OBJECT' or self.itemize:
                    enter = False

                bpy.ops.mesh.primitive_plane_add(enter_editmode=enter,
                                                 align='WORLD',
                                                 location=setpos,
                                                 rotation=setrot,
                                                 size=side,
                                                 scale=(1, 1, 1))
                if self.itemize or self.edit_mode == "OBJECT":
                    bpy.ops.object.shade_smooth()
                    bpy.context.object.data.use_auto_smooth = True

            # -----------------------------------------------------------------------------------------
            # SPHERE
            # -----------------------------------------------------------------------------------------
            elif self.sphere and not self.ke_fitprim_option == "QUADSPHERE":
                bpy.ops.mesh.primitive_uv_sphere_add(
                    segments=self.sphere_seg,
                    ring_count=self.sphere_ring,
                    radius=side,
                    align='WORLD',
                    location=setpos,
                    rotation=setrot)
                if self.itemize or self.edit_mode == "OBJECT":
                    bpy.ops.object.shade_smooth()
                    bpy.context.object.data.use_auto_smooth = True

            # -----------------------------------------------------------------------------------------
            # QUADSPHERE
            # -----------------------------------------------------------------------------------------
            elif self.ke_fitprim_option == "QUADSPHERE":
                cutnr = self.quadsphere_seg

                # calc compensated subd radius from cube
                v1_pos = setpos[0] + side, setpos[1] + side, setpos[2] + side
                rad = sqrt(sum([(a - b)**2 for a, b in zip(setpos, v1_pos)]))
                diff = side / rad

                side = (side * diff)
                distance = side

                bpy.ops.mesh.primitive_box_add(width=side,
                                               depth=side,
                                               height=distance,
                                               align='WORLD',
                                               location=setpos,
                                               rotation=setrot,
                                               name="QuadSphere")

                if self.itemize or self.edit_mode == "OBJECT":
                    bpy.ops.object.mode_set(mode="EDIT")
                    bpy.ops.mesh.subdivide(number_cuts=cutnr, smoothness=1)
                    bpy.ops.mesh.faces_shade_smooth()
                    bpy.ops.object.mode_set(mode="OBJECT")
                else:
                    bpy.ops.mesh.subdivide(number_cuts=cutnr, smoothness=1)
                    bpy.ops.mesh.faces_shade_smooth()

                bpy.ops.ed.undo_push()
                # bpy.context.object.data.use_auto_smooth = as_check

                if self.itemize or self.edit_mode == "OBJECT":
                    bpy.ops.object.shade_smooth()
                    bpy.context.object.data.use_auto_smooth = True

            # -----------------------------------------------------------------------------------------
            # CYLINDER
            # -----------------------------------------------------------------------------------------
            else:
                bpy.ops.mesh.primitive_cylinder_add(vertices=self.cyl_sides,
                                                    radius=side,
                                                    depth=distance * 2,
                                                    enter_editmode=False,
                                                    align='WORLD',
                                                    location=setpos,
                                                    rotation=setrot)

                if self.modal:
                    if self.itemize or self.edit_mode == "OBJECT":
                        bpy.ops.object.mode_set(mode="EDIT")

                    self.settings = (side, distance, setpos, setrot)
                    context.window_manager.modal_handler_add(self)

                    self._timer = context.window_manager.event_timer_add(
                        0.5, window=context.window)

                    args = (self, context, self.screen_x)
                    self._handle = bpy.types.SpaceView3D.draw_handler_add(
                        draw_callback_px, args, 'WINDOW', 'POST_PIXEL')
                    bpy.context.space_data.overlay.show_cursor = False
                    return {'RUNNING_MODAL'}

            if multi_object_mode and not self.itemize:
                second_obj.select_set(state=True)
                obj.select_set(state=True)
                bpy.ops.object.editmode_toggle()
                bpy.ops.object.editmode_toggle()
        else:
            self.report({"INFO"},
                        "FitPrim: Invalid Selection / No Active Element?")

        if not self.select and not self.itemize and not self.edit_mode == "OBJECT":
            bpy.ops.mesh.select_all(action='DESELECT')

        if self.itemize:
            bpy.ops.transform.select_orientation(orientation=og_orientation)
            cursor.location = self.og_cloc
            cursor.rotation_euler = self.og_crot
            bpy.context.active_object.select_set(state=True)

        self.ke_fitprim_itemize = False
        return {"FINISHED"}
Example #45
0
def import_arc(rcurve, bcurve, scale):

    '''
    Parse arc data from Speckle object dictionary
    '''
    #plane = find_key_case_insensitive(rcurve, "plane")
    plane = rcurve.plane
    if not plane:
        return

    origin = plane.origin
    normal = Vector(plane.normal.value)

    #origin = find_key_case_insensitive(plane, "origin")
    #normal = find_key_case_insensitive(plane, "normal")
    #normal = Vector(find_key_case_insensitive(normal, "value"))

    xaxis = plane.xdir
    yaxis = plane.ydir

    radius = rcurve.radius * scale
    startAngle = rcurve.startAngle
    endAngle = rcurve.endAngle

    #xaxis = find_key_case_insensitive(plane, "xdir")
    #yaxis = find_key_case_insensitive(plane, "ydir")

    #radius = find_key_case_insensitive(rcurve, "radius", 0) * scale
    #startAngle = find_key_case_insensitive(rcurve, "startAngle", 0)
    #endAngle = find_key_case_insensitive(rcurve, "endAngle", 0)

    startQuat = Quaternion(normal, startAngle)
    endQuat = Quaternion(normal, endAngle)

    '''
    Get start and end vectors, centre point, angles, etc.
    '''

    r1 = Vector(plane.xdir.value)
    r1.rotate(startQuat)

    r2 = Vector(plane.xdir.value)
    r2.rotate(endQuat)

    c = Vector(plane.origin.value) * scale

    spt = c + r1 * radius
    ept = c + r2 * radius

    #d = radius * (endAngle - startAngle)
    angle = endAngle - startAngle

    t1 = normal.cross(r1)
    #t2 = normal.cross(r2)

    '''
    Initialize arc data and calculate subdivisions
    '''
    arc = bcurve.splines.new('NURBS')

    arc.use_cyclic_u = False

    Ndiv = max(int(math.floor(angle / 0.3)), 2)
    step = angle / float(Ndiv)
    stepQuat = Quaternion(normal, step)
    tan = math.tan(step / 2) * radius

    arc.points.add(Ndiv + 1)

    '''
    Set start and end points
    '''
    arc.points[0].co = (spt.x, spt.y, spt.z, 1)
    arc.points[Ndiv + 1].co = (ept.x, ept.y, ept.z, 1)

    '''
    Set intermediate points
    '''
    for i in range(Ndiv):
        t1 = normal.cross(r1)
        pt = c + r1 * radius + t1 * tan
        arc.points[i + 1].co = (pt.x, pt.y, pt.z, 1)
        r1.rotate(stepQuat)

    '''
    Set curve settings
    '''

    arc.use_endpoint_u = True
    arc.order_u = 3    

    return arc
Example #46
0
def lp_left(bm, lp, wid, vm, wm):
    # pass
    size = len(lp)
    up = wm.inverted() * vm.inverted() * Vector((0, 0, 1))
    lp_off = []
    faces = []
    for i in range(size - 1):
        if i == 0:
            pt = bm.verts[lp[i]].co
            pre = bm.verts[lp[-2]].co
            nxt = bm.verts[lp[1]].co
            pre_ind = lp[size - 2]
            nxt_ind = lp[1]
        else:
            bm.verts.ensure_lookup_table()
            pt = bm.verts[lp[i]].co
            pre = bm.verts[lp[i - 1]].co
            nxt = bm.verts[lp[i + 1]].co
            pre_ind = lp[i - 1]
            nxt_ind = lp[i + 1]

        vec1 = pt - pre
        vec2 = pt - nxt

        mid = vec1.normalized() + vec2.normalized()
        if mid.length < 10e-4:

            up2 = Vector((0, 0, 1))
            mid = up2.cross(vec1)

        else:
            xx = mid.cross(vec1).dot(up)

            if xx > 0:
                mid.negate()

        mid.normalize()
        if pre_ind == nxt_ind:
            mid = (pt - pre).normalized()
            q_a = mathutils.Quaternion((0.0, 0.0, 1.0), math.radians(90.0))
            q_b = mathutils.Quaternion((0.0, 0.0, 1.0), math.radians(-180.0))
            mid.rotate(q_a)
            pt1 = pt + mid * wid
            mid.rotate(q_b)
            pt2 = pt + mid * wid
            new_vert_1 = bm.verts.new(pt1)
            new_vert_2 = bm.verts.new(pt2)
            lp_off.append([new_vert_1, new_vert_2])
        else:
            ang = mid.angle(pre - pt)

            vec_len = wid / (math.sin(ang))
            # print(wid)
            pt = pt + mid * vec_len
            new_vert = bm.verts.new(pt)
            lp_off.append(new_vert)
    lp_off.append(lp_off[0])
    bm.verts.index_update()
    for i in range(len(lp_off) - 1):
        bm.verts.ensure_lookup_table()
        p1 = bm.verts[lp[i]]
        p2 = bm.verts[lp[i + 1]]
        p3 = lp_off[i + 1]
        p4 = lp_off[i]

        if isinstance(p3, list):

            faces.append((p1, p2, p3[0], p4))
            #faces.append((p3[0],p2,p3[1]))
        elif isinstance(p4, list):

            faces.append((p1, p2, p3, p4[1]))
        else:
            faces.append((p1, p2, p3, p4))

    return faces
Example #47
0
    def execute(self, context):
        automerge = False
        self.used_modal = False
        sel_check, place, noloc, place, dupe = False, False, False, False, False
        normal, tangent, place_quat = None, None, None
        mode = bpy.context.mode[:]
        sel_mode = bpy.context.tool_settings.mesh_select_mode[:]
        obj = bpy.context.active_object
        if not obj:
            self.report({"INFO"}, "Unrotator: No Active Object?")
            return {"CANCELLED"}

        if self.ke_unrotator_option == "DUPE":
            dupe, noloc = True, False
        elif self.ke_unrotator_option == "NO_LOC":
            noloc, dupe = True, False

        connect = bpy.context.scene.kekit.unrotator_connect
        nolink = bpy.context.scene.kekit.unrotator_nolink
        nosnap = bpy.context.scene.kekit.unrotator_nosnap
        actual_invert = bpy.context.scene.kekit.unrotator_invert
        self.center = bpy.context.scene.kekit.unrotator_center

        if self.center_override:
            self.center = True

        # Check mouse over target -----------------------------------------------------------------------
        bpy.ops.object.mode_set(mode="OBJECT")
        hit_obj, hit_wloc, hit_normal, hit_face = mouse_raycast(context,
                                                                self.mouse_pos,
                                                                evaluated=True)
        # print("HIT:", hit_obj, hit_face, hit_normal)

        if mode == "OBJECT" and not nosnap:
            self.rot_offset = 0
            self.center = False

        if mode == "EDIT_MESH":
            bpy.ops.object.mode_set(mode="EDIT")

            automerge = bool(context.scene.tool_settings.use_mesh_automerge)
            if automerge:
                context.scene.tool_settings.use_mesh_automerge = False

            # Get selected obj bm
            obj_mtx = obj.matrix_world.copy()
            od = obj.data
            bm = bmesh.from_edit_mesh(od)

            # Check selections
            sel_poly = [p for p in bm.faces if p.select]
            sel_edges = [e for e in bm.edges if e.select]
            sel_verts = [v for v in bm.verts if v.select]

            active_h = bm.select_history.active
            active_point = None

            # Making sure an active element is in the selection
            if sel_mode[1] and sel_edges:
                if active_h not in sel_edges:
                    bm.select_history.add(sel_edges[-1])
                    active_h = bm.select_history.active
                active_point = active_h.verts[0]

            elif sel_mode[0] and sel_verts:
                if active_h not in sel_verts:
                    bm.select_history.add(sel_verts[-1])
                    active_h = bm.select_history.active
                active_point = active_h

            elif sel_mode[2] and sel_poly:
                if active_h not in sel_poly:
                    bm.select_history.add(sel_poly[-1])
                    active_h = bm.select_history.active
                active_point = active_h.verts[0]

            # Verify valid selections
            if len(sel_verts) >= 3 and active_h:
                if sel_mode[0]:
                    sel_check = True
                elif sel_mode[1] and len(sel_edges) >= 2:
                    sel_check = True
                elif sel_mode[2] and len(sel_poly):
                    sel_check = True

            if not sel_check:
                self.report({
                    "INFO"
                }, "Unrotator: Selection Error - Incorrect/No geo Selected? Active Element?"
                            )
                return {"CANCELLED"}

            # Expand Linked, or not ------------------------------------------------------------------
            if connect:
                bpy.ops.mesh.select_linked()
                linked_verts = [v for v in bm.verts if v.select]

            elif not connect:
                linked_verts = sel_verts  # fall-back
                # still calc the linked avg pos
                islands = get_islands(bm, sel_verts)
                for island in islands:
                    if any(v in sel_verts for v in island):
                        linked_verts = island
                        break

            # Linked Selection center pos
            bm.verts.ensure_lookup_table()
            linked_verts_co = [v.co for v in linked_verts]
            avg_pos = obj_mtx @ Vector(average_vector(linked_verts_co))
            sel_cos = [v.co for v in sel_verts]
            avg_sel_pos = obj_mtx @ Vector(average_vector(sel_cos))
            center_d = Vector(avg_sel_pos - avg_pos).normalized()

            # bmdupe
            if dupe:
                bmesh.ops.duplicate(bm,
                                    geom=([v for v in bm.verts if v.select] +
                                          [e for e in bm.edges if e.select] +
                                          [p for p in bm.faces if p.select]))

            # -----------------------------------------------------------------------------------------
            # Check mouse over target - place check
            # -----------------------------------------------------------------------------------------
            if type(hit_face) == int:

                if hit_obj.name == obj.name:
                    bm.faces.ensure_lookup_table()
                    hit_face_verts = bm.faces[hit_face].verts[:]

                    if any(i in hit_face_verts for i in linked_verts):
                        # print("Mouse over same Obj and selected geo - Unrotate only mode")
                        place = False
                    else:
                        # print("Mouse over same Obj but unselected geo - Unrotate & Place in same mesh mode")
                        place = True
                        n, t = self.calc_face_vectors(bm.faces[hit_face],
                                                      obj_mtx, obj,
                                                      len(hit_face_verts))
                        n.negate()
                        place_quat = rotation_from_vector(n, t).to_quaternion()

                        if self.center:
                            bm.faces.ensure_lookup_table()
                            hit_wloc = average_vector(
                                [obj_mtx @ v.co for v in hit_face_verts])

                elif hit_obj.name != obj.name:
                    # print("Mouse over different Object - Unrotate & Place mode")
                    place = True
                    hit_face_verts = hit_obj.data.polygons[
                        hit_face].vertices[:]
                    n, t = self.calc_face_vectors(
                        hit_obj.data.polygons[hit_face], hit_obj.matrix_world,
                        hit_obj, len(hit_face_verts))
                    n.negate()
                    place_quat = rotation_from_vector(n, t).to_quaternion()

                    if self.center:
                        hit_vcos = [
                            hit_obj.matrix_world @ hit_obj.data.vertices[i].co
                            for i in hit_face_verts
                        ]
                        hit_wloc = average_vector(hit_vcos)

            # -----------------------------------------------------------------------------------------
            # Selection processing for normal and tangent vecs
            # -----------------------------------------------------------------------------------------
            if sel_mode[0]:
                # VERT MODE ---------------------------------------------------------------------------
                h = tri_points_order(
                    [sel_verts[0].co, sel_verts[1].co, sel_verts[2].co])
                # Vectors from 3 points, hypoT ignored
                p1, p2, p3 = obj_mtx @ sel_verts[h[0]].co, obj_mtx @ sel_verts[
                    h[1]].co, obj_mtx @ sel_verts[h[2]].co
                v2 = p3 - p1
                v1 = p2 - p1
                normal = v1.cross(v2).normalized()
                tangent = Vector(v1).normalized()
                # Direction flip check against center mass
                d = normal.dot(center_d)
                if d <= 0:
                    normal.negate()

            elif sel_mode[1]:
                # EDGE MODE ---------------------------------------------------------------------------
                bm.edges.ensure_lookup_table()
                # Active edge is tangent
                ev = active_h.verts[:]
                ev1, ev2 = ev[0].co, ev[1].co
                tangent = correct_normal(obj_mtx,
                                         Vector((ev1 - ev2)).normalized())
                # B-tangent is shortest vec to v1
                c = [v for v in sel_verts if v not in ev]
                vecs = [Vector((ev1 - v.co).normalized()) for v in c]
                v2 = correct_normal(obj_mtx, sorted(vecs)[0])
                # Normal is crossp
                normal = tangent.cross(v2).normalized()
                # Direction flip check against center mass
                d = normal.dot(center_d)
                if d <= 0:
                    normal.negate()

            elif sel_mode[2]:
                # POLY MODE ---------------------------------------------------------------------------
                normal, tangent = self.calc_face_vectors(
                    active_h, obj_mtx, obj, len(sel_verts))

            # -----------------------------------------------------------------------------------------
            # Process & Execute transforms!
            # -----------------------------------------------------------------------------------------
            if actual_invert:
                normal.negate()
            if self.inv:
                normal.negate()

            rotm = rotation_from_vector(normal, tangent)
            rq = rotm.to_quaternion()

            if self.rot_offset != 0:
                rq @= Quaternion((0, 0, 1), self.rot_offset)

            if place_quat is not None:
                qd = place_quat @ rq.inverted()
            else:
                # rot to place at bottom
                rq @= Quaternion((1, 0, 0), radians(-180.0))
                qd = rq.rotation_difference(Quaternion())

            rot = qd.to_euler("XYZ")
            rx, ry, rz = rot.x * -1, rot.y * -1, rot.z * -1

            # Old macro method to avoid non-uniform scale issues...slow, but works.
            bpy.ops.transform.rotate(value=rx,
                                     orient_axis='X',
                                     orient_type='GLOBAL',
                                     use_proportional_edit=False,
                                     snap=False,
                                     orient_matrix_type='GLOBAL')
            bpy.ops.transform.rotate(value=ry,
                                     orient_axis='Y',
                                     orient_type='GLOBAL',
                                     use_proportional_edit=False,
                                     snap=False,
                                     orient_matrix_type='GLOBAL')
            bpy.ops.transform.rotate(value=rz,
                                     orient_axis='Z',
                                     orient_type='GLOBAL',
                                     use_proportional_edit=False,
                                     snap=False,
                                     orient_matrix_type='GLOBAL')

            # Calc offset
            nv = [v.co for v in linked_verts]
            npos = obj_mtx @ Vector(average_vector(nv))

            if place and not noloc:
                # Move & offset placement
                d = distance_point_to_plane(obj_mtx @ active_point.co, npos,
                                            hit_normal)
                offset = hit_normal * d
                hit_vec = hit_wloc - (npos + offset)

                bmesh.ops.translate(bm,
                                    vec=hit_vec,
                                    space=obj_mtx,
                                    verts=linked_verts)
                bmesh.update_edit_mesh(obj.data)

            else:
                # Compensate z pos to rot-in-place-ish
                comp = (npos - avg_pos) * -1
                bpy.ops.transform.translate(value=comp)
                bmesh.update_edit_mesh(obj.data)

        elif mode == 'OBJECT':
            # Unrotate only
            if not hit_obj:
                obj.select_set(True)
                obj.rotation_euler = (0, 0, self.rot_offset)

            elif hit_obj.name == obj.name:
                obj.select_set(True)
                obj.rotation_euler = (0, 0, self.rot_offset)

            # Place!
            elif hit_obj.name != obj.name:
                obj.select_set(True)
                self.og_pos = Vector(obj.location)

                if dupe:
                    if nolink:
                        bpy.ops.object.duplicate(linked=False)
                    else:
                        bpy.ops.object.duplicate(linked=True)
                    obj = bpy.context.active_object
                    obj.select_set(True)

                mtx = hit_obj.matrix_world
                eks = hit_obj.data.polygons[hit_face].edge_keys
                vecs = []

                for vp in eks:
                    vc1 = mtx @ hit_obj.data.vertices[vp[0]].co
                    vc2 = mtx @ hit_obj.data.vertices[vp[1]].co
                    vecs.append(vc1 - vc2)

                short_sort = sorted(vecs)
                start_vec = short_sort[0]

                self.setrot = rotation_from_vector(hit_normal,
                                                   start_vec,
                                                   rotate90=True,
                                                   rw=True).to_quaternion()
                self.setrot @= Quaternion((0, 0, 1), self.rot_offset)

                if noloc or nosnap:
                    obj.rotation_euler = self.setrot.to_euler()

                if not noloc:
                    if self.center and nosnap:
                        hit_wloc = hit_obj.matrix_world @ Vector(
                            hit_obj.data.polygons[hit_face].center)
                        obj.location = hit_wloc

                    # no snapping place only
                    obj.location = hit_wloc

                    if not nosnap:
                        obj.rotation_euler = self.setrot.to_euler()
                        self.f_center = hit_obj.matrix_world @ Vector(
                            hit_obj.data.polygons[hit_face].center)
                        self.used_modal = True
                        self.og_snaps = self.get_snap_settings(context)
                        self.set_snap_settings(context, self.modal_snap)
                        context.window_manager.modal_handler_add(self)
                        bpy.ops.transform.translate('INVOKE_DEFAULT')
                        return {'RUNNING_MODAL'}

        # Selection revert for non-face selections
        if not sel_mode[2] and mode != "OBJECT":
            bpy.ops.mesh.select_all(action='DESELECT')

            if sel_mode[1]:
                bm.edges.ensure_lookup_table()
                for v in sel_edges:
                    bm.edges[v.index].select = True
            elif sel_mode[0]:
                bm.verts.ensure_lookup_table()
                for v in sel_verts:
                    bm.verts[v.index].select = True

            bm.select_history.clear()
            bm.select_history.add(active_h)
            bmesh.update_edit_mesh(obj.data)

        if automerge:
            context.scene.tool_settings.use_mesh_automerge = True

        return {"FINISHED"}
Example #48
0
class Turtle:

	def __init__(	self,
					tropism=(0,0,0),
					tropismsize=0,
					angle=radians(30),
					iseed=42 ):
		self.tropism = Vector(tropism).normalized()
		self.magnitude = tropismsize
		self.forward = Vector((1,0,0))
		self.up = Vector((0,0,1))
		self.right = self.forward.cross(self.up)
		self.stack = []
		self.position = Vector((0,0,0))
		self.angle = angle
		self.radius = 0.1
		self.__init_terminals()
		seed(iseed)
		
	def __init_terminals(self):
		"""
		Initialize a map of predefined terminals.
		"""
		self.terminals = {
			'+': self.term_plus,
			'-': self.term_minus,
			'[': self.term_push,
			']': self.term_pop,
			'/': self.term_slash,
			'\\': self.term_backslash,
			'<': self.term_less,
			'>': self.term_greater,
			'&': self.term_amp,
			'!': self.term_expand,
			'@': self.term_shrink,
			'#': self.term_fatten,
			'%': self.term_slink,
			'F': self.term_edge,
			'Q': self.term_quad,
			# '{': self.term_object
			}   
	
	def apply_tropism(self):
		# tropism is a normalized vector
		t = self.tropism * self.magnitude
		tf=self.forward + t
		tf.normalize()
		q = tf.rotation_difference(self.forward)
		self.forward.rotate(q)
		self.up.rotate(q)
		self.right.rotate(q)
		
	def term_plus(self, value=None):
		val = radians(value) if not value is None else self.angle
		r = Matrix.Rotation(val, 4, self.right)
		self.forward.rotate(r)
		self.up.rotate(r)
		
	def term_minus(self, value=None):
		val = radians(value) if not value is None else self.angle
		r = Matrix.Rotation(-val, 4, self.right)
		self.forward.rotate(r)
		self.up.rotate(r)
		
	def term_amp(self, value=30):
		k = (random()-0.5) * value
		self.term_plus(value=k)
		k = (random()-0.5) * value
		self.term_slash(value=k)
		
	def term_slash(self, value=None):
		r = Matrix.Rotation(radians(value) if not value is None
							else self.angle, 4, self.up)
		self.forward.rotate(r)
		self.right.rotate(r)
		
	def term_backslash(self, value=None):
		r = Matrix.Rotation(-radians(value) if not value is None
							else -self.angle, 4, self.up)
		self.forward.rotate(r)
		self.right.rotate(r)
		
	def term_less(self, value=None):
		r = Matrix.Rotation(radians(value) if not value is None
							else self.angle, 4, self.forward)
		self.up.rotate(r)
		self.right.rotate(r)
		
	def term_greater(self, value=None):
		r = Matrix.Rotation(-radians(value) if not value is None
							else -self.angle, 4, self.forward)
		self.up.rotate(r)
		self.right.rotate(r)
		
	def term_pop(self, value=None):
		t = self.stack.pop()
		(	self.forward,
			self.up,
			self.right,
			self.position,
			self.radius ) = t
		
	def term_push(self, value=None):
		t = (	self.forward.copy(),
				self.up.copy(),
				self.right.copy(),
				self.position.copy(),
				self.radius )
		self.stack.append(t)
	
	def term_expand(self, value=1.1):
		self.forward *= value
		self.up *= value
		self.right *= value
		
	def term_shrink(self, value=0.9):
		self.forward *= value
		self.up *= value
		self.right *= value
		
	def term_fatten(self, value=1.1):
		self.radius *= value
		
	def term_slink(self, value=0.9):
		self.radius *= value
		
	def term_edge(self, value=None):
		s = self.position.copy()
		self.apply_tropism()
		self.position += self.forward
		e = self.position.copy()
		return Edge(start=s, end=e, radius=self.radius)
	
	def term_quad(self, value=0.5):
		return Quad(pos=self.position,
					right=self.right,
					up=self.up,
					forward=self.forward )
		
	def term_object(self, value=None, name=None):
		s = self.position.copy()
		self.apply_tropism()
		self.position += self.forward
		return BObject(name=name,
					pos=s,
					right=self.right,
					up=self.up,
					forward=self.forward )
		
	def interpret(self, s):
		"""
		interpret the iterable s, yield Quad, Edge or Object named tuples.
		"""
		print('interpret:',s)
		name=''
		for c in s:
			t = None
			print(c,name)
			if c == '}':
				t = self.term_object(name=name[1:])
				name=''
			elif c == '{' or name != '':
				name += c
				continue
			elif name != '':
				continue
			elif c in self.terminals:
				t = self.terminals[c]()
			print('yield',t)
			if not t is None:
				yield t
    def execute(self, context):

        if self.ke_fitprim_option == "BOX":
            self.boxmode, self.world = True, False
        elif self.ke_fitprim_option == "CYL":
            self.boxmode, self.world = False, False
        elif self.ke_fitprim_option == "SPHERE":
            self.boxmode, self.world, self.sphere = False, False, True
        elif self.ke_fitprim_option == "QUADSPHERE":
            self.boxmode, self.world, self.sphere = False, False, True

        if bpy.context.scene.kekit.fitprim_item:
            self.itemize = True
        else:
            self.itemize = self.ke_fitprim_itemize

        self.modal = bpy.context.scene.kekit.fitprim_modal
        self.cyl_sides = bpy.context.scene.kekit.fitprim_sides
        self.select = bpy.context.scene.kekit.fitprim_select
        self.unit = bpy.context.scene.kekit.fitprim_unit
        self.sphere_ring = bpy.context.scene.kekit.fitprim_sphere_ring
        self.sphere_seg = bpy.context.scene.kekit.fitprim_sphere_seg
        self.quadsphere_seg = bpy.context.scene.kekit.fitprim_quadsphere_seg

        self.edit_mode = bpy.context.mode
        sel_verts = []
        multi_object_mode = False
        # -----------------------------------------------------------------------------------------
        # MULTI OBJECT CHECK & SETUP
        # -----------------------------------------------------------------------------------------
        if self.edit_mode == "EDIT_MESH":
            sel_mode = [b for b in bpy.context.tool_settings.mesh_select_mode]
            multi_object_mode = False
            other_side = []

            sel_obj = [
                o for o in bpy.context.objects_in_mode if o.type == "MESH"
            ]
            if len(sel_obj) == 2:
                multi_object_mode = True

            obj = bpy.context.active_object

            if multi_object_mode:
                second_obj = [o for o in sel_obj if o != obj][0]
                bm2 = bmesh.from_edit_mesh(second_obj.data)
                obj_mtx2 = second_obj.matrix_world.copy()

                # selection double check - just to get some manual selection control w. 2nd active face
                sel_poly2 = [p for p in bm2.faces if p.select]
                if sel_poly2:
                    active_face2 = bm2.faces.active
                    if active_face2 not in sel_poly2:
                        active_face2 = None
                    if active_face2:
                        sel_verts2 = active_face2.verts
                    else:
                        sel_verts2 = sel_poly2[0].verts
                else:
                    sel_verts2 = [v for v in bm2.verts if v.select]

                if not sel_verts2:
                    multi_object_mode = False

                elif sel_mode[0]:
                    # Just for 2-vert mode in multiobj mode
                    ole = sel_verts2[0].link_edges[:]
                    other_side = get_shortest(obj_mtx2, ole)

            obj_mtx = obj.matrix_world.copy()

            bm = bmesh.from_edit_mesh(obj.data)
            bm.faces.ensure_lookup_table()
            bm.verts.ensure_lookup_table()

            sel_verts = [v for v in bm.verts if v.select]

            if sel_mode[2]:
                sel_poly = [p for p in bm.faces if p.select]
                active_face = bm.faces.active
                if active_face not in sel_poly:
                    active_face = None

            side = None
            distance = 0
            vps = []
            normal, setpos, setrot, center = None, None, None, None
            first_island = None
            island_mode = False

        # -----------------------------------------------------------------------------------------
        # NO SELECTION MODE
        # -----------------------------------------------------------------------------------------
        if not sel_verts or self.edit_mode == "OBJECT":
            self.world = True
            screenpos = region_2d_to_location_3d(context.region,
                                                 context.space_data.region_3d,
                                                 self.mouse_pos, (0, 0, 0))
            setpos = screenpos
            side = self.unit
            distance = side
            sel_mode = (True, False, False)

        # -----------------------------------------------------------------------------------------
        # VERT MODE(s)
        # -----------------------------------------------------------------------------------------
        elif sel_mode[0] and len(sel_verts) == 1 and not multi_object_mode:
            # 1-VERT MODE
            self.world = True
            side = get_shortest(obj_mtx, sel_verts[0].link_edges[:])
            distance = side
            setpos = obj_mtx @ sel_verts[0].co

        elif sel_mode[0]:
            # 2+ Vert mode
            if multi_object_mode:
                p1 = obj_mtx @ sel_verts[0].co
                p2 = obj_mtx2 @ sel_verts2[0].co
                side = [other_side]
                con_edges = sel_verts[0].link_edges[:]
                side.append(get_shortest(obj_mtx, con_edges))
                side = sorted(side)[0]
            else:
                p1 = obj_mtx @ sel_verts[0].co
                p2 = obj_mtx @ sel_verts[1].co
                con_edges = sel_verts[0].link_edges[:] + sel_verts[
                    1].link_edges[:]
                side = get_shortest(obj_mtx, con_edges)

            v_1 = p1 - p2
            v_2 = Vector((0, 0, 1))
            if v_1.dot(v_2) < 0:
                v_2 = Vector((1, 0, 0))
            n_v = v_1.cross(v_2).normalized()
            u_v = n_v.cross(v_1).normalized()
            t_v = u_v.cross(n_v).normalized()

            setrot = Matrix((u_v, n_v, t_v)).to_4x4().inverted()
            distance = get_distance(p1, p2)
            setpos = Vector(get_midpoint(p1, p2))

        # -----------------------------------------------------------------------------------------
        # EDGE MODE
        # -----------------------------------------------------------------------------------------
        elif sel_mode[1]:

            one_line, loops, loops2 = False, [], []
            sel_edges = [e for e in bm.edges if e.select]
            vps = [e.verts for e in sel_edges]

            active_edge = bm.select_history.active
            if active_edge:
                active_edge_facenormals = [
                    correct_normal(obj_mtx, p.normal)
                    for p in active_edge.link_faces
                ]
                active_edge_normal = Vector(
                    average_vector(active_edge_facenormals)).normalized()

            # GET ISLANDS
            if multi_object_mode and active_edge:
                # print("multi obj mode") # todo: limited multiobj mode, rework one-for-all?
                sel_edges2 = [e for e in bm2.edges if e.select]
                vps2 = [e.verts for e in sel_edges2]
                p1 = get_loops(vps, legacy=True)
                p2 = get_loops(vps2, legacy=True)
                if p1 and p2:
                    a1, a2 = obj_mtx @ p1[0][0].co, obj_mtx @ p1[0][-1].co
                    b1, b2 = obj_mtx2 @ p2[0][0].co, obj_mtx2 @ p2[0][-1].co
                else:
                    a1, a2 = obj_mtx @ sel_verts[0].co, obj_mtx @ sel_verts[
                        -1].co
                    b1, b2 = obj_mtx2 @ sel_verts2[
                        0].co, obj_mtx2 @ sel_verts2[-1].co

                b_avg = get_midpoint(b1, b2)
                spacing, mp = get_closest_midpoint(a1, a2, b_avg)

                u_v = Vector(a1 - b1).normalized()
                t_v = Vector(a1 - a2).normalized()
                n_v = u_v.cross(t_v).normalized()

                setrot = rotation_from_vector(n_v, t_v, rotate90=True)
                setpos = mp
                side = spacing
                distance = spacing

            elif active_edge:  # same (active) obj island selections
                if len(sel_edges) > 1:
                    if len(sel_edges) == 2 and not bool(
                            set(sel_edges[0].verts).intersection(
                                sel_edges[1].verts)):
                        a1p, a2p = sel_edges[0].verts, sel_edges[1].verts
                        a1, a2 = obj_mtx @ a1p[0].co, obj_mtx @ a1p[1].co
                        # b1, b2 = obj_mtx @ a2p[0].co, obj_mtx @ a2p[1].co
                        b_avg = average_vector(
                            [obj_mtx @ a2p[0].co, obj_mtx @ a2p[1].co])

                        u_v = Vector(a1 - (obj_mtx @ a2p[0].co)).normalized()
                        t_v = Vector(a1 - a2).normalized()
                        n_v = u_v.cross(t_v).normalized()

                        setrot = rotation_from_vector(n_v, t_v, rotate90=True)
                        spacing, mp = get_closest_midpoint(a1, a2, b_avg)
                        setpos = mp
                        side = spacing
                        distance = spacing
                    else:
                        loops = get_loops(vps, legacy=True)

                if len(loops) > 1:
                    # print ("single obj - multi loop", len(loops))
                    # Check for closed loops
                    a_ep1, a_ep2 = loops[0][0], loops[0][-1]
                    b_ep1, b_ep2 = loops[1][0], loops[1][-1]
                    if a_ep1 == a_ep2:
                        a_ep2 = loops[0][-2]
                    if b_ep1 == b_ep2:
                        b_ep2 = loops[1][-2]

                    # get coords & set vals
                    a1, a2 = obj_mtx @ a_ep1.co, obj_mtx @ a_ep2.co
                    b1, b2 = obj_mtx @ b_ep1.co, obj_mtx @ b_ep2.co

                    b_avg = get_midpoint(b1, b2)
                    spacing, mp = get_closest_midpoint(a1, a2, b_avg)

                    u_v = Vector((0, 0, 1))
                    t_v = Vector(a1 - a2).normalized()
                    if u_v.dot(t_v) > 0:
                        u_v = Vector((1, 0, 0))
                    n_v = u_v.cross(t_v).normalized()

                    setrot = rotation_from_vector(n_v, t_v, rotate90=False)
                    setpos = mp
                    side = spacing
                    distance = spacing

                elif len(loops) == 1 or len(sel_edges) == 1:
                    # print ("single obj - single loop")
                    vecs = [(obj_mtx @ vp[0].co) - (obj_mtx @ vp[1].co)
                            for vp in vps]
                    start_vec = vecs[-1]
                    side, sides, center = get_sides(obj_mtx, start_vec, vecs,
                                                    vps)
                    # if len(sel_edges) == 4:
                    # print("FIX")

                    # ONE LINE (ONE EDGE or SINGLE STRAIGHT LINE) MODE------------------------------
                    if len(sides) == 1 and len(sides[0]) == len(sel_verts):
                        # print("1 side --> one line:", sides[0])
                        one_line = sides[0][0], sides[0][-1]

                    elif len(sel_edges) == 1:
                        # print("1 edge --> one line", one_line)
                        one_line = sel_verts

                    if one_line:
                        # print ("one line. center, points, normal:", center, one_line, active_edge_normal)
                        p1, p2 = obj_mtx @ one_line[0].co, obj_mtx @ one_line[
                            1].co
                        t_v = Vector(p1 - p2).normalized()
                        n_v = active_edge_normal

                        setrot = rotation_from_vector(n_v, t_v)
                        distance = get_distance(p1, p2)
                        setpos = get_midpoint(p1, p2)
                        side = distance

                    else:
                        # RECT CHECK SIDES OR NGON MODE--------------------------------------------
                        # print("Edge Rect/Ngon mode. Sides:", len(sides))
                        b_candidate = sides[0][0].link_edges
                        b_candidate = [e.verts for e in b_candidate]
                        b = [
                            v for vp in b_candidate for v in vp
                            if v not in sides[0] and v in sel_verts
                        ]
                        if b:
                            b = obj_mtx @ b[0].co
                            a = obj_mtx @ sides[0][0].co - obj_mtx @ sides[0][
                                1].co
                            b = obj_mtx @ sides[0][0].co - b

                            t_v = Vector(a).normalized()
                            u_v = Vector(b).normalized()
                            n_v = u_v.cross(t_v).normalized()

                            setrot = rotation_from_vector(n_v,
                                                          t_v,
                                                          rotate90=True)
                            distance = side
                            setpos = center
                        else:
                            side = None

        # -----------------------------------------------------------------------------------------
        # FACE MODE
        # -----------------------------------------------------------------------------------------
        elif sel_mode[2] and active_face and sel_poly:

            # GET ISLANDS
            if multi_object_mode and active_face:
                first_island = sel_verts
                point = obj_mtx @ active_face.calc_center_median()

                firstcos = [obj_mtx @ v.co for v in sel_verts]
                secondcos = [obj_mtx2 @ v.co for v in sel_verts2]

                if firstcos and secondcos:
                    island_mode = True
                    distance = point_to_plane(point, firstcos, secondcos)
                    if distance:
                        distance = abs(distance)
                    else:
                        # print("Multi obj Point to plane failed for some reason - using single island mode.")
                        island_mode = False

            else:  # same (active) obj island selections
                first_island, second_island = get_selection_islands(
                    sel_poly, active_face)

                if len(first_island) != 0 and len(second_island) != 0:
                    firstcos = [obj_mtx @ v.co
                                for v in first_island]  # needs order sort
                    secondcos = [obj_mtx @ v.co for v in second_island]
                    distance = point_to_plane(
                        obj_mtx @ active_face.calc_center_median(), firstcos,
                        secondcos)

                    if distance:
                        distance = abs(distance)
                        island_mode = True
                    else:
                        # print("Point to plane failed for some reason - using single island mode.")
                        island_mode = False
                else:
                    # Ngon mode
                    first_island = sel_verts

            # GET PRIMARY SELECTION VALUES
            ngoncheck = len(active_face.verts)
            if sel_mode[2] and len(sel_verts) < 4:
                ngoncheck = 5  # todo: better solution for tris...

            bpy.ops.mesh.select_all(action='DESELECT')

            for v in first_island:
                bm.verts[v.index].select = True

            bmesh.update_edit_mesh(obj.data, True)
            bpy.ops.mesh.select_mode(type='VERT')
            bpy.ops.mesh.select_mode(type='FACE')

            bm.faces.ensure_lookup_table()
            # sel_poly = [p for p in bm.faces if p.select]
            bpy.ops.mesh.region_to_loop()

            sel_edges = [e for e in bm.edges if e.select]
            vps = [e.verts for e in sel_edges]
            vecs = [(obj_mtx @ vp[0].co) - (obj_mtx @ vp[1].co) for vp in vps]

            normal = correct_normal(obj_mtx, active_face.normal)
            start_vec = vecs[-1]
            setrot = rotation_from_vector(normal, start_vec)

            # print("Rectangle or Ngon mode",ngoncheck)
            if ngoncheck <= 4:
                side, sides, center = get_sides(obj_mtx,
                                                start_vec,
                                                vecs,
                                                vps,
                                                legacy=False)
            else:
                side, sides, center = get_sides(obj_mtx,
                                                start_vec,
                                                vecs,
                                                vps,
                                                legacy=True)

        # -----------------------------------------------------------------------------------------
        # PROCESS
        # -----------------------------------------------------------------------------------------
        if side:
            if len(sel_verts) == 0 or sel_mode[0] or sel_mode[1]:
                side *= .5
                distance = distance / 2
                if self.sphere and sel_mode[0]:
                    if not len(sel_verts) == 0:
                        side = distance

            elif sel_mode[2]:
                if island_mode:
                    side *= .5
                    offset = normal * distance * .5
                    distance *= .5
                    if self.sphere:
                        side = distance
                else:
                    side *= .5
                    distance = side
                    offset = normal * side

                setpos = center + offset
                if not island_mode and self.sphere:
                    setpos = setpos - offset

            # SET FINAL ROTATION
            if self.world:
                setrot = (0, 0, 0)
            else:
                setrot = setrot.to_euler()

            # RUN OP
            if not self.edit_mode == "OBJECT":
                bpy.ops.mesh.select_mode(type='FACE')

            if self.itemize:
                bpy.ops.object.mode_set(mode="OBJECT")
                cursor = bpy.context.scene.cursor
                bpy.ops.transform.select_orientation(orientation='GLOBAL')
                cursor.location = setpos
                cursor.rotation_euler = setrot

            if self.boxmode:
                bpy.ops.mesh.primitive_box_add(width=side,
                                               depth=side,
                                               height=distance,
                                               align='WORLD',
                                               location=setpos,
                                               rotation=setrot)
                if self.itemize or self.edit_mode == "OBJECT":
                    bpy.ops.object.shade_smooth()
                    bpy.context.object.data.use_auto_smooth = True

            elif self.sphere and not self.ke_fitprim_option == "QUADSPHERE":
                bpy.ops.mesh.primitive_uv_sphere_add(
                    segments=self.sphere_seg,
                    ring_count=self.sphere_ring,
                    radius=side,
                    align='WORLD',
                    location=setpos,
                    rotation=setrot)
                if self.itemize or self.edit_mode == "OBJECT":
                    bpy.ops.object.shade_smooth()
                    bpy.context.object.data.use_auto_smooth = True

            elif self.ke_fitprim_option == "QUADSPHERE":
                bpy.ops.mesh.primitive_round_cube_add(
                    align='WORLD',
                    location=setpos,
                    rotation=setrot,
                    radius=side,
                    size=(0, 0, 0),
                    arc_div=self.quadsphere_seg,
                    lin_div=0,
                    div_type='CORNERS')

                if self.itemize or self.edit_mode == "OBJECT":
                    bpy.ops.object.shade_smooth()
                    bpy.context.object.data.use_auto_smooth = True

            else:
                bpy.ops.mesh.primitive_cylinder_add(vertices=self.cyl_sides,
                                                    radius=side,
                                                    depth=distance * 2,
                                                    enter_editmode=False,
                                                    align='WORLD',
                                                    location=setpos,
                                                    rotation=setrot)

                if self.modal:
                    if self.itemize or self.edit_mode == "OBJECT":
                        bpy.ops.object.mode_set(mode="EDIT")

                    self.settings = (side, distance, setpos, setrot)
                    context.window_manager.modal_handler_add(self)

                    args = (self, context, self.screen_x)
                    self._handle = bpy.types.SpaceView3D.draw_handler_add(
                        draw_callback_px, args, 'WINDOW', 'POST_PIXEL')
                    bpy.context.space_data.overlay.show_cursor = False
                    return {'RUNNING_MODAL'}

            if multi_object_mode and not self.itemize:
                second_obj.select_set(state=True)
                obj.select_set(state=True)
                bpy.ops.object.editmode_toggle()
                bpy.ops.object.editmode_toggle()

        else:
            self.report({
                "INFO"
            }, "FitPrim: Incorrect Selection? / Edit Mode? / No Active Element?"
                        )

        if not self.select and not self.itemize and not self.edit_mode == "OBJECT":
            bpy.ops.mesh.select_all(action='DESELECT')

        if self.itemize:
            bpy.ops.view3d.snap_cursor_to_center()
            bpy.context.active_object.select_set(state=True)

        self.ke_fitprim_itemize = False
        return {"FINISHED"}
def calvn(pas, alfa, ray, vn):
    vr = Vector((cos(alfa), sin(alfa), 0.0))
    vt = Vector((-ray * sin(alfa), ray * cos(alfa), pas))
    vn = vr.cross(vt)
    vn.normalize()
    return vn
class Line3d(Line):
    """
        3d Line
        mostly a gl enabled for future use in manipulators
        coords are in world space
    """
    def __init__(self, p=None, v=None, p0=None, p1=None, z_axis=None):
        """
            Init by either
            p: Vector or tuple origin
            v: Vector or tuple size and direction
            or
            p0: Vector or tuple 1 point location
            p1: Vector or tuple 2 point location
            Will convert any into Vector 3d
            both optionnals
        """
        if p is not None and v is not None:
            self.p = Vector(p).to_3d()
            self.v = Vector(v).to_3d()
        elif p0 is not None and p1 is not None:
            self.p = Vector(p0).to_3d()
            self.v = Vector(p1).to_3d() - self.p
        else:
            self.p = Vector((0, 0, 0))
            self.v = Vector((0, 0, 0))
        if z_axis is not None:
            self.z_axis = z_axis
        else:
            self.z_axis = Vector((0, 0, 1))

    @property
    def p0(self):
        return self.p

    @property
    def p1(self):
        return self.p + self.v

    @p0.setter
    def p0(self, p0):
        """
            Note: setting p0
            move p0 only
        """
        p1 = self.p1
        self.p = Vector(p0).to_3d()
        self.v = p1 - p0

    @p1.setter
    def p1(self, p1):
        """
            Note: setting p1
            move p1 only
        """
        self.v = Vector(p1).to_3d() - self.p

    @property
    def cross_z(self):
        """
            3d Vector perpendicular on plane xy
            lie on the right side
            p1
            |--x
            p0
        """
        return self.v.cross(Vector((0, 0, 1)))

    @property
    def cross(self):
        """
            3d Vector perpendicular on plane defined by z_axis
            lie on the right side
            p1
            |--x
            p0
        """
        return self.v.cross(self.z_axis)

    def normal(self, t=0):
        """
            3d Vector perpendicular on plane defined by z_axis
            lie on the right side
            p1
            |--x
            p0
        """
        n = Line3d()
        n.p = self.lerp(t)
        n.v = self.cross
        return n

    def sized_normal(self, t, size):
        """
            3d Line perpendicular on plane defined by z_axis and of given size
            positionned at t in current line
            lie on the right side
            p1
            |--x
            p0
        """
        p = self.lerp(t)
        v = size * self.cross.normalized()
        return Line3d(p, v, z_axis=self.z_axis)

    def offset(self, offset):
        """
            offset > 0 on the right part
        """
        return Line3d(self.p + offset * self.cross.normalized(), self.v)

    # unless override, 2d methods should raise NotImplementedError
    def intersect(self, line):
        raise NotImplementedError

    def point_sur_segment(self, pt):
        raise NotImplementedError

    def tangeant(self, t, da, radius):
        raise NotImplementedError
def camera_light_source(use_camera, use_light, object_center_vec, object_size):

    camera_factor = 15.0

    # If chosen a camera is put into the scene.
    if use_camera == True:

        # Assume that the object is put into the global origin. Then, the
        # camera is moved in x and z direction, not in y. The object has its
        # size at distance sqrt(object_size) from the origin. So, move the
        # camera by this distance times a factor of camera_factor in x and z.
        # Then add x, y and z of the origin of the object.
        object_camera_vec = Vector((sqrt(object_size) * camera_factor, 0.0,
                                    sqrt(object_size) * camera_factor))
        camera_xyz_vec = object_center_vec + object_camera_vec

        # Create the camera
        camera_data = bpy.data.cameras.new("A_camera")
        camera_data.lens = 45
        camera_data.clip_end = 500.0
        camera = bpy.data.objects.new("A_camera", camera_data)
        camera.location = camera_xyz_vec
        bpy.context.collection.objects.link(camera)

        # Here the camera is rotated such it looks towards the center of
        # the object. The [0.0, 0.0, 1.0] vector along the z axis
        z_axis_vec = Vector((0.0, 0.0, 1.0))
        # The angle between the last two vectors
        angle = object_camera_vec.angle(z_axis_vec, 0)
        # The cross-product of z_axis_vec and object_camera_vec
        axis_vec = z_axis_vec.cross(object_camera_vec)
        # Rotate 'axis_vec' by 'angle' and convert this to euler parameters.
        # 4 is the size of the matrix.
        camera.rotation_euler = Matrix.Rotation(angle, 4, axis_vec).to_euler()

        # Rotate the camera around its axis by 90° such that we have a nice
        # camera position and view onto the object.
        bpy.ops.object.select_all(action='DESELECT')
        camera.select_set(True)

        # Rotate the camera around its axis 'object_camera_vec' by 90° such
        # that we have a nice camera view onto the object.
        matrix_rotation = Matrix.Rotation(90 / 360 * 2 * pi, 4,
                                          object_camera_vec)
        rotate_object(matrix_rotation, camera)

    # Here a lamp is put into the scene, if chosen.
    if use_light == True:

        # This is the distance from the object measured in terms of %
        # of the camera distance. It is set onto 50% (1/2) distance.
        light_dl = sqrt(object_size) * 15 * 0.5
        # This is a factor to which extend the lamp shall go to the right
        # (from the camera  point of view).
        light_dy_right = light_dl * (3.0 / 4.0)

        # Create x, y and z for the lamp.
        object_light_vec = Vector((light_dl, light_dy_right, light_dl))
        light_xyz_vec = object_center_vec + object_light_vec

        # Create the lamp
        light_data = bpy.data.lights.new(name="A_light", type="SUN")
        light_data.distance = 500.0
        light_data.energy = 3.0
        lamp = bpy.data.objects.new("A_light", light_data)
        lamp.location = light_xyz_vec
        bpy.context.collection.objects.link(lamp)

        # Some settings for the World: a bit ambient occlusion
        bpy.context.scene.world.light_settings.use_ambient_occlusion = True
        bpy.context.scene.world.light_settings.ao_factor = 0.2
        # Some properties for cycles
        lamp.data.use_nodes = True
        lmp_P_BSDF = lamp.data.node_tree.nodes['Emission']
        lmp_P_BSDF.inputs['Strength'].default_value = 5
Example #53
0
 def get_axes(mmd_local_axis_x, mmd_local_axis_z):
     x_axis = Vector(mmd_local_axis_x).normalized().xzy
     z_axis = Vector(mmd_local_axis_z).normalized().xzy
     y_axis = z_axis.cross(x_axis).normalized()
     z_axis = x_axis.cross(y_axis).normalized()  # correction
     return (x_axis, y_axis, z_axis)
def extract_primitives(glTF, blender_mesh, blender_vertex_groups, modifiers,
                       export_settings):
    """
    Extract primitives from a mesh. Polygons are triangulated and sorted by material.

    Furthermore, primitives are split up, if the indices range is exceeded.
    Finally, triangles are also split up/duplicated, if face normals are used instead of vertex normals.
    """
    print_console('INFO', 'Extracting primitive: ' + blender_mesh.name)

    use_tangents = False
    if blender_mesh.uv_layers.active and len(blender_mesh.uv_layers) > 0:
        try:
            blender_mesh.calc_tangents()
            use_tangents = True
        except Exception:
            print_console(
                'WARNING',
                'Could not calculate tangents. Please try to triangulate the mesh first.'
            )

    #

    material_map = {}

    #
    # Gathering position, normal and tex_coords.
    #
    no_material_attributes = {POSITION_ATTRIBUTE: [], NORMAL_ATTRIBUTE: []}

    if use_tangents:
        no_material_attributes[TANGENT_ATTRIBUTE] = []

    #
    # Directory of materials with its primitive.
    #
    no_material_primitives = {
        MATERIAL_ID: '',
        INDICES_ID: [],
        ATTRIBUTES_ID: no_material_attributes
    }

    material_name_to_primitives = {'': no_material_primitives}

    #

    vertex_index_to_new_indices = {}

    material_map[''] = vertex_index_to_new_indices

    #
    # Create primitive for each material.
    #
    for blender_material in blender_mesh.materials:
        if blender_material is None:
            continue

        attributes = {POSITION_ATTRIBUTE: [], NORMAL_ATTRIBUTE: []}

        if use_tangents:
            attributes[TANGENT_ATTRIBUTE] = []

        primitive = {
            MATERIAL_ID: blender_material.name,
            INDICES_ID: [],
            ATTRIBUTES_ID: attributes
        }

        material_name_to_primitives[blender_material.name] = primitive

        #

        vertex_index_to_new_indices = {}

        material_map[blender_material.name] = vertex_index_to_new_indices

    tex_coord_max = 0
    if blender_mesh.uv_layers.active:
        tex_coord_max = len(blender_mesh.uv_layers)

    #

    vertex_colors = {}

    color_index = 0
    for vertex_color in blender_mesh.vertex_colors:
        vertex_color_name = COLOR_PREFIX + str(color_index)
        vertex_colors[vertex_color_name] = vertex_color

        color_index += 1
        if color_index >= GLTF_MAX_COLORS:
            break
    color_max = color_index

    #

    bone_max = 0
    for blender_polygon in blender_mesh.polygons:
        for loop_index in blender_polygon.loop_indices:
            vertex_index = blender_mesh.loops[loop_index].vertex_index
            bones_count = len(blender_mesh.vertices[vertex_index].groups)
            if bones_count > 0:
                if bones_count % 4 == 0:
                    bones_count -= 1
                bone_max = max(bone_max, bones_count // 4 + 1)

    #

    morph_max = 0

    blender_shape_keys = []

    if blender_mesh.shape_keys is not None:
        morph_max = len(blender_mesh.shape_keys.key_blocks) - 1

        for blender_shape_key in blender_mesh.shape_keys.key_blocks:
            if blender_shape_key != blender_shape_key.relative_key:
                blender_shape_keys.append(
                    ShapeKey(
                        blender_shape_key,
                        blender_shape_key.normals_vertex_get(
                        ),  # calculate vertex normals for this shape key
                        blender_shape_key.normals_polygon_get())
                )  # calculate polygon normals for this shape key

    #
    # Convert polygon to primitive indices and eliminate invalid ones. Assign to material.
    #
    for blender_polygon in blender_mesh.polygons:
        export_color = True

        #

        if blender_polygon.material_index < 0 or blender_polygon.material_index >= len(blender_mesh.materials) or \
                blender_mesh.materials[blender_polygon.material_index] is None:
            primitive = material_name_to_primitives['']
            vertex_index_to_new_indices = material_map['']
        else:
            primitive = material_name_to_primitives[blender_mesh.materials[
                blender_polygon.material_index].name]
            vertex_index_to_new_indices = material_map[blender_mesh.materials[
                blender_polygon.material_index].name]
        #

        attributes = primitive[ATTRIBUTES_ID]

        face_normal = blender_polygon.normal
        face_tangent = Vector((0.0, 0.0, 0.0))
        face_bitangent = Vector((0.0, 0.0, 0.0))
        if use_tangents:
            for loop_index in blender_polygon.loop_indices:
                temp_vertex = blender_mesh.loops[loop_index]
                face_tangent += temp_vertex.tangent
                face_bitangent += temp_vertex.bitangent

            face_tangent.normalize()
            face_bitangent.normalize()

        #

        indices = primitive[INDICES_ID]

        loop_index_list = []

        if len(blender_polygon.loop_indices) == 3:
            loop_index_list.extend(blender_polygon.loop_indices)
        elif len(blender_polygon.loop_indices) > 3:
            # Triangulation of polygon. Using internal function, as non-convex polygons could exist.
            polyline = []

            for loop_index in blender_polygon.loop_indices:
                vertex_index = blender_mesh.loops[loop_index].vertex_index
                v = blender_mesh.vertices[vertex_index].co
                polyline.append(Vector((v[0], v[1], v[2])))

            triangles = tessellate_polygon((polyline, ))

            for triangle in triangles:
                loop_index_list.append(
                    blender_polygon.loop_indices[triangle[0]])
                loop_index_list.append(
                    blender_polygon.loop_indices[triangle[2]])
                loop_index_list.append(
                    blender_polygon.loop_indices[triangle[1]])
        else:
            continue

        for loop_index in loop_index_list:
            vertex_index = blender_mesh.loops[loop_index].vertex_index

            if vertex_index_to_new_indices.get(vertex_index) is None:
                vertex_index_to_new_indices[vertex_index] = []

            #

            v = None
            n = None
            t = None
            b = None
            uvs = []
            colors = []
            joints = []
            weights = []

            target_positions = []
            target_normals = []
            target_tangents = []

            vertex = blender_mesh.vertices[vertex_index]

            v = convert_swizzle_location(vertex.co, export_settings)
            if blender_polygon.use_smooth:
                n = convert_swizzle_location(vertex.normal, export_settings)
                if use_tangents:
                    t = convert_swizzle_tangent(
                        blender_mesh.loops[loop_index].tangent,
                        export_settings)
                    b = convert_swizzle_location(
                        blender_mesh.loops[loop_index].bitangent,
                        export_settings)
            else:
                n = convert_swizzle_location(face_normal, export_settings)
                if use_tangents:
                    t = convert_swizzle_tangent(face_tangent, export_settings)
                    b = convert_swizzle_location(face_bitangent,
                                                 export_settings)

            if use_tangents:
                tv = Vector((t[0], t[1], t[2]))
                bv = Vector((b[0], b[1], b[2]))
                nv = Vector((n[0], n[1], n[2]))

                if (nv.cross(tv)).dot(bv) < 0.0:
                    t[3] = -1.0

            if blender_mesh.uv_layers.active:
                for tex_coord_index in range(0, tex_coord_max):
                    uv = blender_mesh.uv_layers[tex_coord_index].data[
                        loop_index].uv
                    uvs.append([uv.x, 1.0 - uv.y])

            #

            if color_max > 0 and export_color:
                for color_index in range(0, color_max):
                    color_name = COLOR_PREFIX + str(color_index)
                    color = vertex_colors[color_name].data[loop_index].color
                    colors.append([
                        color_srgb_to_scene_linear(color[0]),
                        color_srgb_to_scene_linear(color[1]),
                        color_srgb_to_scene_linear(color[2]), 1.0
                    ])

            #

            bone_count = 0

            if blender_vertex_groups is not None and vertex.groups is not None and len(
                    vertex.groups) > 0 and export_settings[
                        gltf2_blender_export_keys.SKINS]:
                joint = []
                weight = []
                vertex_groups = vertex.groups
                if not export_settings['gltf_all_vertex_influences']:
                    # sort groups by weight descending
                    vertex_groups = sorted(vertex.groups,
                                           key=attrgetter('weight'),
                                           reverse=True)
                for group_element in vertex_groups:

                    if len(joint) == 4:
                        bone_count += 1
                        joints.append(joint)
                        weights.append(weight)
                        joint = []
                        weight = []

                    #

                    joint_weight = group_element.weight
                    if joint_weight <= 0.0:
                        continue

                    #

                    vertex_group_index = group_element.group
                    vertex_group_name = blender_vertex_groups[
                        vertex_group_index].name

                    joint_index = None

                    if modifiers is not None:
                        modifiers_dict = {m.type: m for m in modifiers}
                        if "ARMATURE" in modifiers_dict:
                            armature = modifiers_dict["ARMATURE"].object
                            skin = gltf2_blender_gather_skins.gather_skin(
                                armature, export_settings)
                            for index, j in enumerate(skin.joints):
                                if j.name == vertex_group_name:
                                    joint_index = index
                                    break

                    #
                    if joint_index is not None:
                        joint.append(joint_index)
                        weight.append(joint_weight)

                if len(joint) > 0:
                    bone_count += 1

                    for fill in range(0, 4 - len(joint)):
                        joint.append(0)
                        weight.append(0.0)

                    joints.append(joint)
                    weights.append(weight)

            for fill in range(0, bone_max - bone_count):
                joints.append([0, 0, 0, 0])
                weights.append([0.0, 0.0, 0.0, 0.0])

            #

            if morph_max > 0 and export_settings[
                    gltf2_blender_export_keys.MORPH]:
                for morph_index in range(0, morph_max):
                    blender_shape_key = blender_shape_keys[morph_index]

                    v_morph = convert_swizzle_location(
                        blender_shape_key.shape_key.data[vertex_index].co,
                        export_settings)

                    # Store delta.
                    v_morph -= v

                    target_positions.append(v_morph)

                    #

                    n_morph = None

                    if blender_polygon.use_smooth:
                        temp_normals = blender_shape_key.vertex_normals
                        n_morph = (temp_normals[vertex_index * 3 + 0],
                                   temp_normals[vertex_index * 3 + 1],
                                   temp_normals[vertex_index * 3 + 2])
                    else:
                        temp_normals = blender_shape_key.polygon_normals
                        n_morph = (temp_normals[blender_polygon.index * 3 + 0],
                                   temp_normals[blender_polygon.index * 3 + 1],
                                   temp_normals[blender_polygon.index * 3 + 2])

                    n_morph = convert_swizzle_location(n_morph,
                                                       export_settings)

                    # Store delta.
                    n_morph -= n

                    target_normals.append(n_morph)

                    #

                    if use_tangents:
                        rotation = n_morph.rotation_difference(n)

                        t_morph = Vector((t[0], t[1], t[2]))

                        t_morph.rotate(rotation)

                        target_tangents.append(t_morph)

            #
            #

            create = True

            for current_new_index in vertex_index_to_new_indices[vertex_index]:
                found = True

                for i in range(0, 3):
                    if attributes[POSITION_ATTRIBUTE][current_new_index * 3 +
                                                      i] != v[i]:
                        found = False
                        break

                    if attributes[NORMAL_ATTRIBUTE][current_new_index * 3 +
                                                    i] != n[i]:
                        found = False
                        break

                if use_tangents:
                    for i in range(0, 4):
                        if attributes[TANGENT_ATTRIBUTE][current_new_index * 4
                                                         + i] != t[i]:
                            found = False
                            break

                if not found:
                    continue

                for tex_coord_index in range(0, tex_coord_max):
                    uv = uvs[tex_coord_index]

                    tex_coord_id = TEXCOORD_PREFIX + str(tex_coord_index)
                    for i in range(0, 2):
                        if attributes[tex_coord_id][current_new_index * 2 +
                                                    i] != uv[i]:
                            found = False
                            break

                if export_color:
                    for color_index in range(0, color_max):
                        color = colors[color_index]

                        color_id = COLOR_PREFIX + str(color_index)
                        for i in range(0, 3):
                            # Alpha is always 1.0 - see above.
                            current_color = attributes[color_id][
                                current_new_index * 4 + i]
                            if color_srgb_to_scene_linear(
                                    current_color) != color[i]:
                                found = False
                                break

                if export_settings[gltf2_blender_export_keys.SKINS]:
                    for bone_index in range(0, bone_max):
                        joint = joints[bone_index]
                        weight = weights[bone_index]

                        joint_id = JOINTS_PREFIX + str(bone_index)
                        weight_id = WEIGHTS_PREFIX + str(bone_index)
                        for i in range(0, 4):
                            if attributes[joint_id][current_new_index * 4 +
                                                    i] != joint[i]:
                                found = False
                                break
                            if attributes[weight_id][current_new_index * 4 +
                                                     i] != weight[i]:
                                found = False
                                break

                if export_settings[gltf2_blender_export_keys.MORPH]:
                    for morph_index in range(0, morph_max):
                        target_position = target_positions[morph_index]
                        target_normal = target_normals[morph_index]
                        if use_tangents:
                            target_tangent = target_tangents[morph_index]

                        target_position_id = MORPH_POSITION_PREFIX + str(
                            morph_index)
                        target_normal_id = MORPH_NORMAL_PREFIX + str(
                            morph_index)
                        target_tangent_id = MORPH_TANGENT_PREFIX + str(
                            morph_index)
                        for i in range(0, 3):
                            if attributes[target_position_id][
                                    current_new_index * 3 +
                                    i] != target_position[i]:
                                found = False
                                break
                            if attributes[target_normal_id][
                                    current_new_index * 3 +
                                    i] != target_normal[i]:
                                found = False
                                break
                            if use_tangents:
                                if attributes[target_tangent_id][
                                        current_new_index * 3 +
                                        i] != target_tangent[i]:
                                    found = False
                                    break

                if found:
                    indices.append(current_new_index)

                    create = False
                    break

            if not create:
                continue

            new_index = 0

            if primitive.get('max_index') is not None:
                new_index = primitive['max_index'] + 1

            primitive['max_index'] = new_index

            vertex_index_to_new_indices[vertex_index].append(new_index)

            #
            #

            indices.append(new_index)

            #

            attributes[POSITION_ATTRIBUTE].extend(v)
            attributes[NORMAL_ATTRIBUTE].extend(n)
            if use_tangents:
                attributes[TANGENT_ATTRIBUTE].extend(t)

            if blender_mesh.uv_layers.active:
                for tex_coord_index in range(0, tex_coord_max):
                    tex_coord_id = TEXCOORD_PREFIX + str(tex_coord_index)

                    if attributes.get(tex_coord_id) is None:
                        attributes[tex_coord_id] = []

                    attributes[tex_coord_id].extend(uvs[tex_coord_index])

            if export_color:
                for color_index in range(0, color_max):
                    color_id = COLOR_PREFIX + str(color_index)

                    if attributes.get(color_id) is None:
                        attributes[color_id] = []

                    attributes[color_id].extend(colors[color_index])

            if export_settings[gltf2_blender_export_keys.SKINS]:
                for bone_index in range(0, bone_max):
                    joint_id = JOINTS_PREFIX + str(bone_index)

                    if attributes.get(joint_id) is None:
                        attributes[joint_id] = []

                    attributes[joint_id].extend(joints[bone_index])

                    weight_id = WEIGHTS_PREFIX + str(bone_index)

                    if attributes.get(weight_id) is None:
                        attributes[weight_id] = []

                    attributes[weight_id].extend(weights[bone_index])

            if export_settings[gltf2_blender_export_keys.MORPH]:
                for morph_index in range(0, morph_max):
                    target_position_id = MORPH_POSITION_PREFIX + str(
                        morph_index)

                    if attributes.get(target_position_id) is None:
                        attributes[target_position_id] = []

                    attributes[target_position_id].extend(
                        target_positions[morph_index])

                    target_normal_id = MORPH_NORMAL_PREFIX + str(morph_index)

                    if attributes.get(target_normal_id) is None:
                        attributes[target_normal_id] = []

                    attributes[target_normal_id].extend(
                        target_normals[morph_index])

                    if use_tangents:
                        target_tangent_id = MORPH_TANGENT_PREFIX + str(
                            morph_index)

                        if attributes.get(target_tangent_id) is None:
                            attributes[target_tangent_id] = []

                        attributes[target_tangent_id].extend(
                            target_tangents[morph_index])

    #
    # Add primitive plus split them if needed.
    #

    result_primitives = []

    for material_name, primitive in material_name_to_primitives.items():
        export_color = True

        #

        indices = primitive[INDICES_ID]

        if len(indices) == 0:
            continue

        position = primitive[ATTRIBUTES_ID][POSITION_ATTRIBUTE]
        normal = primitive[ATTRIBUTES_ID][NORMAL_ATTRIBUTE]
        if use_tangents:
            tangent = primitive[ATTRIBUTES_ID][TANGENT_ATTRIBUTE]
        tex_coords = []
        for tex_coord_index in range(0, tex_coord_max):
            tex_coords.append(primitive[ATTRIBUTES_ID][TEXCOORD_PREFIX +
                                                       str(tex_coord_index)])
        colors = []
        if export_color:
            for color_index in range(0, color_max):
                tex_coords.append(primitive[ATTRIBUTES_ID][COLOR_PREFIX +
                                                           str(color_index)])
        joints = []
        weights = []
        if export_settings[gltf2_blender_export_keys.SKINS]:
            for bone_index in range(0, bone_max):
                joints.append(primitive[ATTRIBUTES_ID][JOINTS_PREFIX +
                                                       str(bone_index)])
                weights.append(primitive[ATTRIBUTES_ID][WEIGHTS_PREFIX +
                                                        str(bone_index)])

        target_positions = []
        target_normals = []
        target_tangents = []
        if export_settings[gltf2_blender_export_keys.MORPH]:
            for morph_index in range(0, morph_max):
                target_positions.append(
                    primitive[ATTRIBUTES_ID][MORPH_POSITION_PREFIX +
                                             str(morph_index)])
                target_normals.append(
                    primitive[ATTRIBUTES_ID][MORPH_NORMAL_PREFIX +
                                             str(morph_index)])
                if use_tangents:
                    target_tangents.append(
                        primitive[ATTRIBUTES_ID][MORPH_TANGENT_PREFIX +
                                                 str(morph_index)])

        #

        count = len(indices)

        if count == 0:
            continue

        max_index = max(indices)

        #

        # NOTE: Values used by some graphics APIs as "primitive restart" values are disallowed.
        # Specifically, the value 65535 (in UINT16) cannot be used as a vertex index.
        # https://github.com/KhronosGroup/glTF/issues/1142
        # https://github.com/KhronosGroup/glTF/pull/1476/files

        range_indices = 65535

        #

        if max_index >= range_indices:
            #
            # Splitting result_primitives.
            #

            # At start, all indices are pending.
            pending_attributes = {POSITION_ATTRIBUTE: [], NORMAL_ATTRIBUTE: []}

            if use_tangents:
                pending_attributes[TANGENT_ATTRIBUTE] = []

            pending_primitive = {
                MATERIAL_ID: material_name,
                INDICES_ID: [],
                ATTRIBUTES_ID: pending_attributes
            }

            pending_primitive[INDICES_ID].extend(indices)

            pending_attributes[POSITION_ATTRIBUTE].extend(position)
            pending_attributes[NORMAL_ATTRIBUTE].extend(normal)
            if use_tangents:
                pending_attributes[TANGENT_ATTRIBUTE].extend(tangent)
            tex_coord_index = 0
            for tex_coord in tex_coords:
                pending_attributes[TEXCOORD_PREFIX +
                                   str(tex_coord_index)] = tex_coord
                tex_coord_index += 1
            if export_color:
                color_index = 0
                for color in colors:
                    pending_attributes[COLOR_PREFIX + str(color_index)] = color
                    color_index += 1
            if export_settings[gltf2_blender_export_keys.SKINS]:
                joint_index = 0
                for joint in joints:
                    pending_attributes[JOINTS_PREFIX +
                                       str(joint_index)] = joint
                    joint_index += 1
                weight_index = 0
                for weight in weights:
                    pending_attributes[WEIGHTS_PREFIX +
                                       str(weight_index)] = weight
                    weight_index += 1
            if export_settings[gltf2_blender_export_keys.MORPH]:
                morph_index = 0
                for target_position in target_positions:
                    pending_attributes[MORPH_POSITION_PREFIX +
                                       str(morph_index)] = target_position
                    morph_index += 1
                morph_index = 0
                for target_normal in target_normals:
                    pending_attributes[MORPH_NORMAL_PREFIX +
                                       str(morph_index)] = target_normal
                    morph_index += 1
                if use_tangents:
                    morph_index = 0
                    for target_tangent in target_tangents:
                        pending_attributes[MORPH_TANGENT_PREFIX +
                                           str(morph_index)] = target_tangent
                        morph_index += 1

            pending_indices = pending_primitive[INDICES_ID]

            # Continue until all are processed.
            while len(pending_indices) > 0:

                process_indices = pending_primitive[INDICES_ID]
                max_index = max(process_indices)

                pending_indices = []

                #
                #

                all_local_indices = []

                for i in range(0, (max_index // range_indices) + 1):
                    all_local_indices.append([])

                #
                #

                # For all faces ...
                for face_index in range(0, len(process_indices), 3):

                    written = False

                    face_min_index = min(process_indices[face_index + 0],
                                         process_indices[face_index + 1],
                                         process_indices[face_index + 2])
                    face_max_index = max(process_indices[face_index + 0],
                                         process_indices[face_index + 1],
                                         process_indices[face_index + 2])

                    # ... check if it can be but in a range of maximum indices.
                    for i in range(0, (max_index // range_indices) + 1):
                        offset = i * range_indices

                        # Yes, so store the primitive with its indices.
                        if face_min_index >= offset and face_max_index < offset + range_indices:
                            all_local_indices[i].extend([
                                process_indices[face_index + 0],
                                process_indices[face_index + 1],
                                process_indices[face_index + 2]
                            ])

                            written = True
                            break

                    # If not written, the triangle face has indices from different ranges.
                    if not written:
                        pending_indices.extend([
                            process_indices[face_index + 0],
                            process_indices[face_index + 1],
                            process_indices[face_index + 2]
                        ])

                # Only add result_primitives, which do have indices in it.
                for local_indices in all_local_indices:
                    if len(local_indices) > 0:
                        current_primitive = extract_primitive_floor(
                            pending_primitive, local_indices, use_tangents)

                        result_primitives.append(current_primitive)

                        print_console(
                            'DEBUG',
                            'Adding primitive with splitting. Indices: ' +
                            str(len(current_primitive[INDICES_ID])) +
                            ' Vertices: ' + str(
                                len(current_primitive[ATTRIBUTES_ID]
                                    [POSITION_ATTRIBUTE]) // 3))

                # Process primitive faces having indices in several ranges.
                if len(pending_indices) > 0:
                    pending_primitive = extract_primitive_pack(
                        pending_primitive, pending_indices, use_tangents)

                    print_console(
                        'DEBUG', 'Creating temporary primitive for splitting')

        else:
            #
            # No splitting needed.
            #
            result_primitives.append(primitive)

            print_console(
                'DEBUG', 'Adding primitive without splitting. Indices: ' +
                str(len(primitive[INDICES_ID])) + ' Vertices: ' +
                str(len(primitive[ATTRIBUTES_ID][POSITION_ATTRIBUTE]) // 3))

    print_console('INFO', 'Primitives created: ' + str(len(result_primitives)))

    return result_primitives
Example #55
0
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 = 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"]
            bpy.data.materials.new(name=key)
            bpy.data.materials[key].diffuse_color = atom_data[key]["color"]
            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 = [
            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)

    # 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 = [[1.4 * atom_data["bond"]["radius"] * x for x in v_obj],
                     [-1.4 * atom_data["bond"]["radius"] * x for x in v_obj]]
        elif bond["order"] == 3:
            trans = [[0] * 3,
                     [2.2 * atom_data["bond"]["radius"] * x for x in v_obj],
                     [-2.2 * atom_data["bond"]["radius"] * 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 = [
                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 = [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()
Example #56
0
def import_xyz(Ball_type,
               Ball_azimuth,
               Ball_zenith,
               Ball_radius_factor,
               radiustype,
               Ball_distance_factor,
               put_to_center,
               put_to_center_all,
               use_camera,
               use_lamp,
               filepath_xyz):

    # List of materials
    atom_material_list = []

    # ------------------------------------------------------------------------
    # INITIALIZE THE ELEMENT LIST

    read_elements()

    # ------------------------------------------------------------------------
    # READING DATA OF ATOMS

    Number_of_total_atoms = read_xyz_file(filepath_xyz,
                                                       radiustype)

    # We show the atoms of the first frame.
    first_frame = ALL_FRAMES[0]

    # ------------------------------------------------------------------------
    # MATERIAL PROPERTIES FOR ATOMS

    # Create first a new list of materials for each type of atom
    # (e.g. hydrogen)
    for atoms_of_one_type in first_frame:
        # Take the first atom
        atom = atoms_of_one_type[0]
        material = bpy.data.materials.new(atom.name)
        material.name = atom.name
        material.diffuse_color = atom.color
        atom_material_list.append(material)

    # Now, we go through all atoms and give them a material. For all atoms ...
    for atoms_of_one_type in first_frame:
        for atom in atoms_of_one_type:
            # ... and all materials ...
            for material in atom_material_list:
                # ... select the correct material for the current atom via
                # comparison of names ...
                if atom.name in material.name:
                    # ... and give the atom its material properties.
                    # However, before we check if it is a vacancy
                    # The vacancy is represented by a transparent cube.
                    if atom.name == "Vacancy":
                        material.transparency_method = 'Z_TRANSPARENCY'
                        material.alpha = 1.3
                        material.raytrace_transparency.fresnel = 1.6
                        material.raytrace_transparency.fresnel_factor = 1.6
                        material.use_transparency = True
                    # The atom gets its properties.
                    atom.material = material

    # ------------------------------------------------------------------------
    # TRANSLATION OF THE STRUCTURE TO THE ORIGIN

    # It may happen that the structure in a XYZ file already has an offset


    # If chosen, the structure is put into the center of the scene
    # (only the first frame).
    if put_to_center == True and put_to_center_all == False:

        sum_vec = Vector((0.0,0.0,0.0))

        # Sum of all atom coordinates
        for atoms_of_one_type in first_frame:
            sum_vec = sum([atom.location for atom in atoms_of_one_type], sum_vec)

        # Then the average is taken
        sum_vec = sum_vec / Number_of_total_atoms

        # After, for each atom the center of gravity is substracted
        for atoms_of_one_type in first_frame:
            for atom in atoms_of_one_type:
                atom.location -= sum_vec

    # If chosen, the structure is put into the center of the scene
    # (all frames).
    if put_to_center_all == True:

        # For all frames
        for frame in ALL_FRAMES:

            sum_vec = Vector((0.0,0.0,0.0))

            # Sum of all atom coordinates
            for (i, atoms_of_one_type) in enumerate(frame):

                # This is a guarantee that only the total number of atoms of the
                # first frame is used. Condition is, so far, that the number of
                # atoms in a xyz file is constant. However, sometimes the number
                # may increase (or decrease). If it decreases, the addon crashes.
                # If it increases, only the tot number of atoms of the first frame
                # is used.
                # By time, I will allow varying atom numbers ... but this takes
                # some time ...
                if i >= Number_of_total_atoms:
                    break

                sum_vec = sum([atom.location for atom in atoms_of_one_type], sum_vec)

            # Then the average is taken
            sum_vec = sum_vec / Number_of_total_atoms

            # After, for each atom the center of gravity is substracted
            for atoms_of_one_type in frame:
                for atom in atoms_of_one_type:
                    atom.location -= sum_vec


    # ------------------------------------------------------------------------
    # SCALING

    # Take all atoms and adjust their radii and scale the distances.
    for atoms_of_one_type in first_frame:
        for atom in atoms_of_one_type:
            atom.location *= Ball_distance_factor

    # ------------------------------------------------------------------------
    # DETERMINATION OF SOME GEOMETRIC PROPERTIES

    # In the following, some geometric properties of the whole object are
    # determined: center, size, etc.
    sum_vec = Vector((0.0,0.0,0.0))

    # First the center is determined. All coordinates are summed up ...
    for atoms_of_one_type in first_frame:
        sum_vec = sum([atom.location for atom in atoms_of_one_type], sum_vec)

    # ... and the average is taken. This gives the center of the object.
    object_center_vec = sum_vec / Number_of_total_atoms

    # Now, we determine the size.The farthest atom from the object center is
    # taken as a measure. The size is used to place well the camera and light
    # into the scene.

    object_size_vec = []
    for atoms_of_one_type in first_frame:
        object_size_vec += [atom.location - object_center_vec for atom in atoms_of_one_type]

    object_size = 0.0
    object_size = max(object_size_vec).length

    # ------------------------------------------------------------------------
    # CAMERA AND LAMP
    camera_factor = 20.0

    # If chosen a camera is put into the scene.
    if use_camera == True:

        # Assume that the object is put into the global origin. Then, the
        # camera is moved in x and z direction, not in y. The object has its
        # size at distance sqrt(object_size) from the origin. So, move the
        # camera by this distance times a factor of camera_factor in x and z.
        # Then add x, y and z of the origin of the object.
        object_camera_vec = Vector((sqrt(object_size) * camera_factor,
                                    0.0,
                                    sqrt(object_size) * camera_factor))
        camera_xyz_vec = object_center_vec + object_camera_vec

        # Create the camera
        current_layers=bpy.context.scene.layers
        camera_data = bpy.data.cameras.new("A_camera")
        camera_data.lens = 45
        camera_data.clip_end = 500.0
        camera = bpy.data.objects.new("A_camera", camera_data)
        camera.location = camera_xyz_vec
        camera.layers = current_layers
        bpy.context.scene.objects.link(camera)

        # Here the camera is rotated such it looks towards the center of
        # the object. The [0.0, 0.0, 1.0] vector along the z axis
        z_axis_vec             = Vector((0.0, 0.0, 1.0))
        # The angle between the last two vectors
        angle                  = object_camera_vec.angle(z_axis_vec, 0)
        # The cross-product of z_axis_vec and object_camera_vec
        axis_vec               = z_axis_vec.cross(object_camera_vec)
        # Rotate 'axis_vec' by 'angle' and convert this to euler parameters.
        # 4 is the size of the matrix.
        camera.rotation_euler  = Matrix.Rotation(angle, 4, axis_vec).to_euler()

        # Rotate the camera around its axis by 90° such that we have a nice
        # camera position and view onto the object.
        bpy.ops.object.select_all(action='DESELECT')
        camera.select = True
        bpy.ops.transform.rotate(value=(90.0*2*pi/360.0),
                                 axis=object_camera_vec,
                                 constraint_axis=(False, False, False),
                                 constraint_orientation='GLOBAL',
                                 mirror=False, proportional='DISABLED',
                                 proportional_edit_falloff='SMOOTH',
                                 proportional_size=1, snap=False,
                                 snap_target='CLOSEST', snap_point=(0, 0, 0),
                                 snap_align=False, snap_normal=(0, 0, 0),
                                 release_confirm=False)

    # Here a lamp is put into the scene, if chosen.
    if use_lamp == True:

        # This is the distance from the object measured in terms of %
        # of the camera distance. It is set onto 50% (1/2) distance.
        lamp_dl = sqrt(object_size) * 15 * 0.5
        # This is a factor to which extend the lamp shall go to the right
        # (from the camera  point of view).
        lamp_dy_right = lamp_dl * (3.0/4.0)

        # Create x, y and z for the lamp.
        object_lamp_vec = Vector((lamp_dl,lamp_dy_right,lamp_dl))
        lamp_xyz_vec = object_center_vec + object_lamp_vec

        # Create the lamp
        current_layers=bpy.context.scene.layers
        lamp_data = bpy.data.lamps.new(name="A_lamp", type="POINT")
        lamp_data.distance = 500.0
        lamp_data.energy = 3.0
        lamp_data.shadow_method = 'RAY_SHADOW'
        lamp = bpy.data.objects.new("A_lamp", lamp_data)
        lamp.location = lamp_xyz_vec
        lamp.layers = current_layers
        bpy.context.scene.objects.link(lamp)

        bpy.context.scene.world.light_settings.use_ambient_occlusion = True
        bpy.context.scene.world.light_settings.ao_factor = 0.2


    # ------------------------------------------------------------------------
    # DRAWING THE ATOMS

    bpy.ops.object.select_all(action='DESELECT')

    # For each list of atoms of ONE type (e.g. Hydrogen)
    for atoms_of_one_type in first_frame:

        # Create first the vertices composed of the coordinates of all
        # atoms of one type
        atom_vertices = []
        for atom in atoms_of_one_type:
            # In fact, the object is created in the World's origin.
            # This is why 'object_center_vec' is substracted. At the end
            # the whole object is translated back to 'object_center_vec'.
            atom_vertices.append( atom.location - object_center_vec )

        # Build the mesh
        atom_mesh = bpy.data.meshes.new("Mesh_"+atom.name)
        atom_mesh.from_pydata(atom_vertices, [], [])
        atom_mesh.update()
        new_atom_mesh = bpy.data.objects.new(atom.name, atom_mesh)
        bpy.context.scene.objects.link(new_atom_mesh)

        # Now, build a representative sphere (atom)
        current_layers=bpy.context.scene.layers

        if atom.name == "Vacancy":
            bpy.ops.mesh.primitive_cube_add(
                            view_align=False, enter_editmode=False,
                            location=(0.0, 0.0, 0.0),
                            rotation=(0.0, 0.0, 0.0),
                            layers=current_layers)
        else:
            # NURBS balls
            if Ball_type == "0":
                bpy.ops.surface.primitive_nurbs_surface_sphere_add(
                            view_align=False, enter_editmode=False,
                            location=(0,0,0), rotation=(0.0, 0.0, 0.0),
                            layers=current_layers)
            # UV balls
            elif Ball_type == "1":
                bpy.ops.mesh.primitive_uv_sphere_add(
                            segments=Ball_azimuth, ring_count=Ball_zenith,
                            size=1, view_align=False, enter_editmode=False,
                            location=(0,0,0), rotation=(0, 0, 0),
                            layers=current_layers)
            # Meta balls
            elif Ball_type == "2":
                bpy.ops.object.metaball_add(type='BALL', view_align=False,
                            enter_editmode=False, location=(0, 0, 0),
                            rotation=(0, 0, 0), layers=current_layers)

        ball = bpy.context.scene.objects.active
        ball.scale  = (atom.radius*Ball_radius_factor,) * 3

        if atom.name == "Vacancy":
            ball.name = "Cube_"+atom.name
        else:
            ball.name = "Ball (NURBS)_"+atom.name
        ball.active_material = atom.material
        ball.parent = new_atom_mesh
        new_atom_mesh.dupli_type = 'VERTS'
        # The object is back translated to 'object_center_vec'.
        new_atom_mesh.location = object_center_vec
        STRUCTURE.append(new_atom_mesh)

    # ------------------------------------------------------------------------
    # SELECT ALL LOADED OBJECTS

    bpy.ops.object.select_all(action='DESELECT')
    obj = None
    for obj in STRUCTURE:
        obj.select = True
    # activate the last selected object (perhaps another should be active?)
    if obj:
        bpy.context.scene.objects.active = obj
Example #57
0
class GlLine(GlBaseLine):
    """
        2d/3d Line
    """
    def __init__(self, d=3, p=None, v=None, p0=None, p1=None, z_axis=None):
        """
            d=3 use 3d coords, d=2 use 2d pixels coords
            Init by either
            p: Vector or tuple origin
            v: Vector or tuple size and direction
            or
            p0: Vector or tuple 1 point location
            p1: Vector or tuple 2 point location
            Will convert any into Vector 3d
            both optionnals
        """
        if p is not None and v is not None:
            self.p = Vector(p)
            self.v = Vector(v)
        elif p0 is not None and p1 is not None:
            self.p = Vector(p0)
            self.v = Vector(p1) - self.p
        else:
            self.p = Vector((0, 0, 0))
            self.v = Vector((0, 0, 0))
        if z_axis is not None:
            self.z_axis = z_axis
        else:
            self.z_axis = Vector((0, 0, 1))
        GlBaseLine.__init__(self, d)

    @property
    def p0(self):
        return self.p

    @property
    def p1(self):
        return self.p + self.v

    @p0.setter
    def p0(self, p0):
        """
            Note: setting p0
            move p0 only
        """
        p1 = self.p1
        self.p = Vector(p0)
        self.v = p1 - p0

    @p1.setter
    def p1(self, p1):
        """
            Note: setting p1
            move p1 only
        """
        self.v = Vector(p1) - self.p

    @property
    def length(self):
        return self.v.length

    @property
    def angle(self):
        return atan2(self.v.y, self.v.x)

    @property
    def cross(self):
        """
            Vector perpendicular on plane defined by z_axis
            lie on the right side
            p1
            |--x
            p0
        """
        return self.v.cross(self.z_axis)

    def normal(self, t=0):
        """
            Line perpendicular on plane defined by z_axis
            lie on the right side
            p1
            |--x
            p0
        """
        n = GlLine()
        n.p = self.lerp(t)
        n.v = self.cross
        return n

    def sized_normal(self, t, size):
        """
            GlLine perpendicular on plane defined by z_axis and of given size
            positionned at t in current line
            lie on the right side
            p1
            |--x
            p0
        """
        n = GlLine()
        n.p = self.lerp(t)
        n.v = size * self.cross.normalized()
        return n

    def lerp(self, t):
        """
            Interpolate along segment
            t parameter [0, 1] where 0 is start of arc and 1 is end
        """
        return self.p + self.v * t

    def offset(self, offset):
        """
            offset > 0 on the right part
        """
        self.p += offset * self.cross.normalized()

    def point_sur_segment(self, pt):
        """ point_sur_segment (2d)
            point: Vector 3d
            t: param t de l'intersection sur le segment courant
            d: distance laterale perpendiculaire positif a droite
        """
        dp = (pt - self.p).to_2d()
        v2d = self.v.to_2d()
        dl = v2d.length
        d = (self.v.x * dp.y - self.v.y * dp.x) / dl
        t = (v2d * dp) / (dl * dl)
        return t > 0 and t < 1, d, t

    @property
    def pts(self):
        return [self.p0, self.p1]
	def execute(self, context):
		curvepoints = []
		curveobj = context.active_object
		
		bpy.ops.object.empty_add(type='PLAIN_AXES')
		parentobj = context.active_object
		parentobj.name = 'CurveForce'
		
		if len(curveobj.data.splines[0].bezier_points) > 1:
			curvepoints = [v.co for v in curveobj.data.splines[0].bezier_points]
		else:
			curvepoints = [v.co for v in curveobj.data.splines[0].points]
		
		curveobj.parent = parentobj
		context.active_object.select = False
		curveobj.select = True
		
		previousnormal = Vector([0.0,0.0,0.0])
		
		lastStrength = context.window_manager.curveForce_strength
		lastDistance = context.window_manager.curveForce_maxDist
		
		for i in range(len(curvepoints)):
			cpoint = Vector([curvepoints[i][0],curvepoints[i][1],curvepoints[i][2]])
			
			bpy.ops.object.empty_add(type='SINGLE_ARROW',location=(cpoint))
			context.active_object.name = 'ForceObj'
			# turn into forcefield
			bpy.ops.object.forcefield_toggle()
			context.active_object.field.type = 'WIND'
			
			if context.window_manager.curveForce_trailout:
				if i > 0:
					lastStrength = lastStrength * 0.9
					lastDistance = lastDistance * 0.9
			
			context.active_object.field.strength = lastStrength
			context.active_object.field.use_max_distance = True
			context.active_object.field.distance_max = lastDistance
			context.active_object.field.falloff_power = context.window_manager.curveForce_falloffPower
			
			# get the curve's direction between points
			tempnorm = Vector([0,0,0])
			if (i < len(curvepoints) - 1):
				cpoint2 = Vector([curvepoints[i + 1][0],curvepoints[i + 1][1],curvepoints[i + 1][2]])
				tempnorm = cpoint - cpoint2
				if i > 0:
					if abs(previousnormal.length) > 0.0:
						tempnorm = (tempnorm + previousnormal) / 2.0
				previousnormal = tempnorm
			else:
				if curveobj.data.splines[0].use_cyclic_u or curveobj.data.splines[0].use_cyclic_u:
					cpoint2 = Vector([curvepoints[0][0],curvepoints[0][1],curvepoints[0][2]])
					tempnorm = cpoint - cpoint2
					if abs(previousnormal.length) > 0.0:
						tempnorm = (tempnorm + previousnormal) / 2.0
					previousnormal = tempnorm
				else:
					cpoint2 = Vector([curvepoints[i - 1][0],curvepoints[i - 1][1],curvepoints[i - 1][2]])
					tempnorm = cpoint2 - cpoint
					if abs(previousnormal.length) > 0.0:
						tempnorm = (tempnorm + previousnormal) / 2.0
					previousnormal = tempnorm
			
			if abs(tempnorm.length) > 0.0:
				z = Vector((0,0,1))
				angle = tempnorm.angle(z)
				axis = z.cross(tempnorm)
				mat = Matrix.Rotation(angle, 4, axis)
				mat_world = context.active_object.matrix_world * mat
				context.active_object.matrix_world = mat_world
			
			context.active_object.parent = parentobj
			
		
		return {'FINISHED'}
Example #59
0
  def grow(self):

    from numpy import sum, all, ones

    self.itt += 1

    stp = self.stp
    killzone = self.killzone
    noise = self.noise

    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,killzone)

    nv,ns = dvs.shape

    self.select_seed()
    bm = self.get_bmesh()
    nodes = [v for v in bm.verts]

    for i,jj in vs_map.items():

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

      v.normalize()

      # this is flawed. particularly for "flat" geometries
      p,pn = self.closest_point_on_geom(nodes[i].co)
      projected = pn.cross(v.cross(pn))
      projected += random_unit_vector()*noise
      projected.normalize()

      new = nodes[i].co + projected*stp

      verts = [nodes[i]]
      new_vert = bmesh.ops.extrude_vert_indiv(
        bm,
        verts=verts)['verts'].pop()
      new_vert.co = new

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

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

        print('merging:',len(ii),'deleted source:',j)
        new_verts = []
        for i in ii:
          new = bmesh.ops.extrude_vert_indiv(
            bm,
            verts=[nodes[i]])['verts'].pop()
          new.co = self.sources[j]
          new_verts.append(new)

        bmesh.ops.remove_doubles(bm,verts=new_verts,dist=0.01)

        self.dead_sources.append(self.sources[j])

    self.to_mesh()

    self.sources = self.sources[mask,:]
    return
Example #60
0
    def execute(self, context):
        obj = context.active_object

        bpy.context.space_data.pivot_point = 'INDIVIDUAL_ORIGINS'

        bpy.ops.object.mode_set(mode='OBJECT')

        if len(bpy.context.selected_objects) > 1:

            first_obj = bpy.context.active_object

            obj_a, obj_b = context.selected_objects

            second_obj = obj_a if obj_b == first_obj else obj_b

            bpy.data.objects[second_obj.name].select = False

            if context.mode == 'EDIT_MESH':

                print(self)
                self.report({'INFO'},
                            "Please select a source and a target object")

            else:

                bpy.ops.object.duplicate_move()

                for i in range(self.set_origin):
                    bpy.ops.object.cubefront_side_minus_z()

                bpy.context.active_object.name = "Dummy"

                bpy.ops.object.parent_clear(type='CLEAR_KEEP_TRANSFORM')

                bpy.ops.object.transform_apply(location=True,
                                               rotation=True,
                                               scale=True)

                copy_cursor = bpy.context.scene.cursor_location.copy()

                bm = bmesh.new()
                bm.from_mesh(obj.data)

                selected_faces = [f for f in bm.faces if f.select]

                for face in selected_faces:

                    face_location = face.calc_center_median()

                    loc_world_space = obj.matrix_world * Vector(face_location)

                    z = Vector((0, 0, 1))

                    angle = face.normal.angle(z)

                    axis = z.cross(face.normal)

                    bpy.ops.object.select_all(action='DESELECT')

                    bpy.context.scene.objects.active = bpy.data.objects[
                        second_obj.name]

                    bpy.data.objects[second_obj.name].select = True

                    for i in range(self.dupli_linked):
                        bpy.ops.object.duplicate_move_linked(
                            OBJECT_OT_duplicate={
                                "linked": True,
                                "mode": 'TRANSLATION'
                            })

                    for i in range(self.dupli):
                        bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={
                            "linked": False,
                            "mode": 'TRANSLATION'
                        })

                        for i in range(self.set_origin):
                            bpy.ops.tp_ops.cubefront_side_minus_z()

                    bpy.context.scene.cursor_location = loc_world_space
                    bpy.ops.view3d.snap_selected_to_cursor()

                    bpy.ops.transform.rotate(value=angle, axis=axis)

                bm.to_mesh(obj.data)
                bm.free()

                bpy.context.scene.cursor_location = copy_cursor

                bpy.ops.object.select_all(action='DESELECT')

                bpy.context.scene.objects.active = bpy.data.objects["Dummy"]
                bpy.data.objects["Dummy"].select = True

                bpy.ops.object.delete(use_global=False)

                bpy.ops.object.select_all(action='DESELECT')

                bpy.context.scene.objects.active = bpy.data.objects[
                    second_obj.name]

                bpy.data.objects[second_obj.name].select = True

                for i in range(self.set_edit):

                    bpy.context.scene.objects.active = bpy.data.objects[
                        first_obj.name]
                    bpy.ops.object.mode_set(mode='EDIT')

                    print(self)
                    self.report({'INFO'}, "Editmode")

        else:
            print(self)
            self.report({'INFO'}, "Please select a source and a target object")

        return {"FINISHED"}