# Fifth Blender 4.91 Tutorial
# This tutorial will cover how Blender BPY Modules, aliases, types, briefly touch on classes,
# and includes a function for creating a moon or an asteroid!

# The bpy Module
# The contents of the bpy module are divided into submodules:
bpy
['app', 'context', 'data', 'msgbus', 'ops', 'path', 'props', 'types', 'utils']

# The modules in pink you will not likely used at all:
# bpy.app - This module contains application values that remain 
# unchanged during runtime, debug information.

# bpy.msgbus - The message bus system can be used to receive notifications
# when properties of Blender datablocks are changed via the data API.

# bpy.props — Defines properties to extend Blender’s internal data. The 
# result of these functions is used to assign properties to classes 
# registered with Blender and can’t be used directly.

# bpy.utils - utility functions specific to blender but not associated 
# with blenders internal data.

# bpy.path - Similar scope to os.path, containing utility functions for dealing 
# with paths in Blender.

bpy.context
# Contains settings like the current 3D mode, which objects are selected, and so on. 
bpy.context:
['active_annotation_layer', 'active_bone', 'active_editable_fcurve', 
'active_gpencil_frame', 'active_gpencil_layer', 'active_object', 
'active_operator', 'active_pose_bone', 'annotation_data', 
'annotation_data_owner', 'area', 'bl_rna', 'blend_data', 'collection', 
'copy', 'edit_object', 'editable_bones', 'editable_fcurves', 
'editable_gpencil_layers', 'editable_gpencil_strokes', 'editable_objects', 
'engine', 'evaluated_depsgraph_get', 'gizmo_group', 'gpencil_data', 
'gpencil_data_owner', 'image_paint_object', 'layer_collection', 'mode', 
'object', 'objects_in_mode', 'objects_in_mode_unique_data', 
'particle_edit_object', 'pose_object', 'preferences', 'region', 
'region_data', 'rna_type', 'scene', 'screen', 'sculpt_object', 
'selectable_objects', 'selected_bones', 'selected_editable_bones', 
'selected_editable_fcurves', 'selected_editable_keyframes', 
'selected_editable_objects', 'selected_editable_sequences', 
'selected_nla_strips', 'selected_objects', 'selected_pose_bones', 
'selected_pose_bones_from_active_object', 'selected_sequences', 
'selected_visible_fcurves', 'sequences', 'space_data', 'tool_settings', 
'vertex_paint_object', 'view_layer', 'visible_bones', 'visible_fcurves', 
'visible_gpencil_layers', 'visible_objects', 'visible_pose_bones', 
'weight_paint_object', 'window', 'window_manager', 'workspace']

bpy.data
# Access to Blender/Python internal data contained in 
# the current blend-file for: objects, meshes, materials, textures, etc.
bpy.data:
['actions', 'armatures', 'batch_remove', 'bl_rna', 'brushes', 
'cache_files', 'cameras', 'collections', 'curves', 'filepath', 'fonts', 
'grease_pencils', 'images', 'is_dirty', 'is_saved', 'lattices', 
'libraries', 'lightprobes', 'lights', 'linestyles', 'masks', 'materials', 
'meshes', 'metaballs', 'movieclips', 'node_groups', 'objects', 
'orphans_purge', 'paint_curves', 'palettes', 'particles', 'rna_type', 
'scenes', 'screens', 'shape_keys', 'sounds', 'speakers', 'texts', 
'textures', 'use_autopack', 'user_map', 'version', 'volumes', 
'window_managers', 'workspaces', 'worlds']

bpy.ops
# Provides python access to calling operators, including 
# operators written in C, Python or macros. 
bpy.ops:
['action', 'anim', 'armature', 'boid', 'brush', 'btool', 'buttons', 
'cachefile', 'camera', 'clip', 'cloth', 'collection', 'console', 
'constraint', 'curve', 'cycles', 'dpaint', 'ed', 'export_anim', 
'export_mesh', 'export_scene', 'file', 'fluid', 'font', 'geometry', 
'gizmogroup', 'gpencil', 'graph', 'image', 'import_anim', 'import_curve', 
'import_mesh', 'import_scene', 'info', 'lattice', 'marker', 'mask', 
'material', 'mball', 'mesh', 'nla', 'node', 'object', 'outliner', 'paint', 
'paintcurve', 'palette', 'particle', 'pose', 'poselib', 'preferences', 
'ptcache', 'render', 'rigidbody', 'safe_areas', 'scene', 'screen', 
'script', 'sculpt', 'sequencer', 'simulation', 'sound', 'surface', 'text', 
'texture', 'transform', 'ui', 'uv', 'view2d', 'view3d', 'wm', 'workspace', 
'world']

bpy.types
# bpy.types — information about the types of the objects
bpy.types:
# Not listing this one, the list is huge, if you want to see it, 
# go to the command prompt and type in dir(bpy.types)

mathutils
['Color', 'Euler', 'Matrix', 'Quaternion', 'Vector', 'bvhtree', 'geometry', 'interpolate', 'kdtree', 'noise']

# Color: RGB colours and conversion to/from HSV space (no alpha channel).
mathutils.Color()

# Euler: represents rotations as Euler angles, rotation angles about X, Y and Z 
# axes. Prone to gimbal lock. (I have never had an issue with gimbal lock when
controlling rotations with code.)
mathutils.Euler()

# Matrix: represents any kind of linear transformation.
mathutils.Matrix()

# Quaternion: A way of representing rotations. No gimbal lock.
mathutils.Quaternion()

# Vector: represents a 2D or 3D coordinate.
mathutils.Vector()

# BVH tree structures for proximity searches and ray casts on geometry.
mathutils.bvhtree.BVHTree()

# Blender geometry module
mathutils.geometry
['area_tri', 'barycentric_transform', 'box_fit_2d', 'box_pack_2d', 
'closest_point_on_tri', 'convex_hull_2d', 'delaunay_2d_cdt', 
'distance_point_to_plane', 'interpolate_bezier', 'intersect_line_line', 
'intersect_line_line_2d', 'intersect_line_plane', 'intersect_line_sphere', 
'intersect_line_sphere_2d', 'intersect_plane_plane', 'intersect_point_line', 
'intersect_point_quad_2d', 'intersect_point_tri', 'intersect_point_tri_2d', 
'intersect_ray_tri', 'intersect_sphere_sphere_2d', 'intersect_tri_tri_2d', 
'normal', 'points_in_planes', 'tessellate_polygon', 'volume_tetrahedron']

# mathutils.interpolate
mathutils.interpolate.poly_3d_calc()
# Blender interpolate module
# Calculate barycentric weights for a point on a polygon.

# mathutils.kdtree
mathutils.kdtree.KDTree()
# Generic 3-dimensional kd-tree to perform spatial searches. 
# Find points within a radius of the 3d cursor

# Blender noise module
mathutils.noise
['cell', 'cell_vector', 'fractal', 'hetero_terrain', 'hybrid_multi_fractal', 
'multi_fractal', 'noise', 'noise_vector', 'random', 'random_unit_vector', 
'random_vector', 'ridged_multi_fractal', 'seed_set', 'turbulence', 
'turbulence_vector', 'variable_lacunarity', 'voronoi']

# How to list the  objects in your scene
list(bpy.data.objects)

# Aliases - Some variables - modules are available as:
# C - Quick access to bpy.context.
# D - Quick access to bpy.data.

# or create your own aliases
import mathutils
vect1 = mathutils.Vector()

Moon = bpy.data.objects["Moon"]
Moon.delta_location
Moon.delta_location = mathutils.Vector((1, 1, 1))
Moon.delta_location = (0, 0, 0)  # <--- This is a tuple, not a vector

# Get current mode
bpy.context.mode

# Get type of a variable
x = 40
type(x)
x = 40.0
type(x)
t = True
type(t)
v = (1, 4, 3)
type(v)
dir(bpy.types)

# Short example of a class:
class Point:
  def __init__(self):
      self.x = 0
      self.y = 0
      self.z = 0

# A class is like a cookbook; you do not use the class itself is not the ingredients, 
# (you would not throw the cookbook in the pot would you?) 
# but it holds the keys to creating something awesome.  So when you want to use
# the class you would NOT type in:
Point.x  -  that will not work!
First, you initialize the class:
p = Point()
# Now, whenever you want to access what is stored in one of the three places within
# the class (p.x, p.y or p.z)
p.x
p.x = 4

import math  # you can then use these in operations!
n = math.sqrt(p.x**2 +  p.y**2 + p.z **2)

# This makes coding much easier, you are able to maintain control and understand the 
# code as it gets more complex.

p.x%2  Mod operator (%) returns what is left after division by the number to the right
p.x%3

I just wanted to touch shortly on classes this time around, later you will see a more in depth
example.

# Moon / Asteroid Creator:
By rotating a small sphere around a larger sphere, then using a boolean operator to create
a crater shape, you can create a Moon-like or Astroid-like shape.  You can have a lot of
fun just playing around with the different settings on this function and view the different
results.  You probably want to save the original settings though, I spent a few hours playing
around with it to determine what settings appear to work best!
import bpy
from mathutils import Vector, Matrix
import random
import math
from math import sin, cos

x=0; y=0; z=0;

# Move active object to origin is default, but can 
# be moved to any vector (x,y,z) coordinate
def set_pivot(coordinates=Vector()):
    ob = bpy.context.active_object
    mw = ob.matrix_world
    o = mw.inverted() @ Vector(coordinates)
    ob.data.transform(Matrix.Translation(-o))
    mw.translation = coordinates

# Now has an option to either deselect all, or keep other 
# things selected, which allows using this for doing booleans 
def obyname(name, deselectall=True):
    for ob in bpy.context.window.scene.objects:
        if(deselectall):
            ob.select_set(False)
            for obj in bpy.context.selected_objects:
                obj.select_set(False)
        if(ob.name==name):
            bpy.context.view_layer.objects.active = ob
            ob = bpy.context.active_object
            ob.select_set(True)
    return ob

# Moon project
moonradius = 6
def createmoon():
    bpy.context.scene.cursor.location = (0,0,0)
    bpy.ops.mesh.primitive_uv_sphere_add(segments=64, ring_count=64, radius=moonradius, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(1, 1, 1))
    ob = bpy.context.active_object
    ob.name = "Moon"

def createcraters(cratercount=30):
    x = 0
    yval=0
    zval=0
    while(x<cratercount):
        # Create smaller sphere to make crater
        if((x%9)==0): # Create more smaller craters, every nineth crater is larger
            scale = random.randrange(11, 17)*.1 
            print("large crater")
        else: 
            scale = random.randrange(6, 10)*.1 
            print("small crater")
        craterdepth = scale * .36   # z-scale sets crater depth
        bpy.ops.mesh.primitive_uv_sphere_add(segments=16, ring_count=11, align='WORLD', location=(moonradius + .02, 0, 0), scale=(scale, scale, craterdepth))
        bpy.ops.transform.rotate(value=1.5708, orient_axis='Y')
        bpy.context.active_object.name = 'crater'
        set_pivot()
        if((x%6)==0):  # Every sixth crater can be closer to poles
            yval = random.randrange(-157, 157) * .01   # radians 1.57 = 90 degrees
        else:
            yval = random.randrange(-79, 79) * .01 # Create more in equatorial regions
        # Radians 1.57/2 = 45 degrees
        zval = random.randrange(0, 628) * .01  # 6.28 radians is one rotation
        if(zval>7):
            zval = 0
        bpy.ops.transform.rotate(value=yval, orient_axis='Y', orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', constraint_axis=(True, True, True))
        bpy.ops.transform.rotate(value=zval, orient_axis='Z', orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', constraint_axis=(True, True, True))
        # Note that here we are NOT de-selecting all so the crater part 
        # remains selected for the boolean operation
        ob = obyname('Moon', False)
        bpy.ops.object.modifier_add(type='BOOLEAN')
        bpy.context.object.modifiers["Boolean"].operation = 'DIFFERENCE'
        bpy.context.object.modifiers["Boolean"].object = bpy.data.objects["crater"]
        bpy.ops.object.modifier_apply(modifier="Boolean")
        ob = obyname('crater', True)
        bpy.ops.object.delete(use_global=False, confirm=False)
        x +=1

# If Moon has already been created, do nothing, skip if Moon exists.
# This allows you to add additional craters if you want more!
create = True  
for ob in bpy.context.window.scene.objects:
    if(ob.name=='Moon'):
        create = False

if(create):    
    createmoon()    

createcraters(20)