Docs: add a utility to generate an updated code-layout diagram
Creates an image with a short description of each directory. See the PR for the current state of the diagram. Ref !106475
This commit is contained in:
1135
tools/utils_doc/code_layout_diagram.py
Normal file
1135
tools/utils_doc/code_layout_diagram.py
Normal file
@@ -0,0 +1,1135 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
This script generates a graphic of Blender's code layout:
|
||||
|
||||
./blender.bin -b --factory-startup --python ./tools/utils_doc/code_layout_diagram.py
|
||||
"""
|
||||
# This is an update to the historic: https://download.blender.org/ftp/ideasman42/pics/code_layout_historic.jpg
|
||||
# Originally located ar: `www.blender.org/bf/codelayout.jpg` (now broken).
|
||||
|
||||
__all__ = (
|
||||
"main",
|
||||
)
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
import os
|
||||
import sys
|
||||
import bpy
|
||||
|
||||
from mathutils import (
|
||||
Euler,
|
||||
Vector,
|
||||
)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Generic Globals
|
||||
|
||||
BASE_DIR = os.path.normpath(os.path.dirname(__file__))
|
||||
ROOT_DIR = os.path.normpath(os.path.join(BASE_DIR, "..", ".."))
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Data
|
||||
|
||||
PAGE_WIDTH = 4.5
|
||||
PAGE_WIDTH_HALF = PAGE_WIDTH / 2.0
|
||||
|
||||
# RESOLUTION_SCALE = 0.25 # For quick test renders.
|
||||
RESOLUTION_SCALE = 1.0
|
||||
RESOLUTION_X = 3200
|
||||
|
||||
OUTPUT_IMAGE_FILE = True
|
||||
OUTPUT_BLEND_FILE = False
|
||||
|
||||
# Can't use `Inter.woff2` as it contains overlapping glyphs.
|
||||
FONT_FILE_DEFAULT = os.path.join(ROOT_DIR, "release", "datafiles", "fonts", "Noto Sans CJK Regular.woff2")
|
||||
FONT_FILE_MONO = os.path.join(ROOT_DIR, "release", "datafiles", "fonts", "DejaVuSansMono.woff2")
|
||||
|
||||
# Apply some offsets, needed as the "default" font has strange scaling.
|
||||
FONT_STYLE_SETTINGS = {
|
||||
"default": {"scale": 1.8, "space_line": 0.5},
|
||||
"mono": {"scale": 1.0, "space_line": 1.0},
|
||||
}
|
||||
|
||||
VFONT_FROM_STYLE = {
|
||||
"default": None,
|
||||
"mono": None,
|
||||
}
|
||||
|
||||
MATERIAL_FROM_COLOR = {
|
||||
"black": None,
|
||||
"grey": None,
|
||||
}
|
||||
|
||||
MATERIAL_INDEX_BLACK = 0
|
||||
MATERIAL_INDEX_GREY = 1
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Graphics
|
||||
|
||||
ARROW_DOWN = ((0, 0), (-1, 1.5), (-0.3, 1.5), (-0.3, 3.3), (0.3, 3.3), (0.3, 1.5), (1, 1.5),)
|
||||
ARROW_DOWN_AND_SIDEWAYS = (
|
||||
(0, 0), (-1, 1.5), (-0.3, 1.5), (-0.3, 2.7), (-1.5, 2.7), (-1.5, 2), (-3, 3), (-1.5, 4), (-1.5, 3.3), (1.5, 3.3),
|
||||
(1.5, 4), (3, 3), (1.5, 2), (1.5, 2.7), (0.3, 2.7), (0.3, 1.5), (1, 1.5),
|
||||
)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Directory Layout Definition
|
||||
|
||||
@dataclass
|
||||
class SectionData:
|
||||
heading: str
|
||||
source_code_base: str
|
||||
source_code_call_sibling_modules: bool
|
||||
source_code_dirs: list
|
||||
|
||||
|
||||
SECTIONS = (
|
||||
SectionData(
|
||||
heading="Application startup",
|
||||
source_code_call_sibling_modules=False,
|
||||
source_code_base="source/",
|
||||
source_code_dirs=[
|
||||
("creator", "Blender's main() function, initialization & argument handling."),
|
||||
("blender", "Contains the majority of Blender's functionality."),
|
||||
],
|
||||
),
|
||||
SectionData(
|
||||
heading="Editor definitions, drawing, interaction",
|
||||
source_code_call_sibling_modules=True,
|
||||
source_code_base="source/blender/editors/",
|
||||
source_code_dirs=[
|
||||
("space_action", "Animation editor for actions."),
|
||||
("space_buttons", "Properties editor."),
|
||||
("space_clip", "Movie clip editor."),
|
||||
("space_console", "Python interactive console."),
|
||||
("space_file", "File selection."),
|
||||
("space_graph", "Animation editor for F-Curves."),
|
||||
("space_image", "Image editor & texture painting."),
|
||||
("space_info", "Info space (for logging)."),
|
||||
("space_nla", "Animation non-linear-action editor."),
|
||||
("space_node", "Node editor for compositor, geometry and other nodes."),
|
||||
("space_outliner", "Outliner editor."),
|
||||
("space_script", "Unused script space (historic)."),
|
||||
("space_sequencer", "Video sequence editor."),
|
||||
("space_spreadsheet", "Spreadsheet data editor."),
|
||||
("space_statusbar", "Window status bar."),
|
||||
("space_text", "Text editor."),
|
||||
("space_topbar", "Window file menu."),
|
||||
("space_userpref", "User preferences."),
|
||||
("space_view3d", "3D viewport."),
|
||||
],
|
||||
),
|
||||
SectionData(
|
||||
heading="Editor utilities",
|
||||
source_code_call_sibling_modules=True,
|
||||
source_code_base="source/blender/editors/",
|
||||
source_code_dirs=[
|
||||
("datafiles", "Definitions for data-files used by editors: icons, material previews & startup file."),
|
||||
("gizmo_library", "Shared shapes for gizmo drawing."),
|
||||
("screen", "Screen operators & manipulation, including windowing operations."),
|
||||
("space_api", "Space shared API's."),
|
||||
("undo", "Undo operators and high-level API."),
|
||||
("util", "Shared utilities including numeric input, image access, gizmo drawing & selection logic."),
|
||||
],
|
||||
),
|
||||
SectionData(
|
||||
heading="Tools",
|
||||
source_code_call_sibling_modules=True,
|
||||
source_code_base="source/blender/editors/",
|
||||
source_code_dirs=[
|
||||
("animation", "Utilities for key-framing and animation editors."),
|
||||
("armature", "Tools for editing armature object data."),
|
||||
("asset", "Tools for the asset editors."),
|
||||
("curve", "Legacy curve object support."),
|
||||
("curves", "Curve object support."),
|
||||
("geometry", "Generic utilities for dealing with geometry (meshes, curves ... etc)."),
|
||||
("gpencil_legacy", "Tools for editing grease pencil, legacy now only used for \"Annotations\"."),
|
||||
("grease_pencil", "Operators and utilities to manipulate grease mencil."),
|
||||
("id_management", "Operators and utilities for managing ID data blocks."),
|
||||
("include", "Shared includes for editor modules which share logic with other editors."),
|
||||
("interface", "Graphical user interface logic for widgets and their interactions."),
|
||||
("io", "Operators for importing & exporting 3D content such as geometry, grease pencil & animation."),
|
||||
("lattice", "Tools for editing lattice object data."),
|
||||
("mask", "Tools for editing and animating 2D masks."),
|
||||
("mesh", "Tools for editing mesh object data."),
|
||||
("metaball", "Tools for editing meta-ball object data."),
|
||||
("object", "Tools for editing objects and collections."),
|
||||
("physics", "Tools for physics including particles, rigidbody & dynamic paint."),
|
||||
("pointcloud", "Tools for point-cloud manipulation."),
|
||||
("render", "Render operators, viewing & viewport preview."),
|
||||
("scene", "Tools for scene manipulation (add/copy/remove)."),
|
||||
("sculpt_paint", "Tools for sculpting and painting."),
|
||||
("sound", "Tools for sound manipulation, packing, unpacking & mixing down audio."),
|
||||
("transform", "Interactive transform and transform implementations (rotate, scal, move ... etc)."),
|
||||
("uvedit", "Mesh UV editing tools for copy/paste, selection & packing."),
|
||||
],
|
||||
),
|
||||
SectionData(
|
||||
heading="Window, events, operators, core interaction",
|
||||
source_code_call_sibling_modules=False,
|
||||
source_code_base="source/blender/",
|
||||
source_code_dirs=[
|
||||
("windowmanager", "General window, event handling."),
|
||||
],
|
||||
),
|
||||
SectionData(
|
||||
heading="General Blender API's",
|
||||
source_code_call_sibling_modules=True,
|
||||
source_code_base="source/blender/",
|
||||
source_code_dirs=[
|
||||
("asset_system", "Asset system back-end (asset representations, asset libraries, catalogs, etc.)."),
|
||||
("blendthumb", "Thumbnail extraction for the BLEND file-format."),
|
||||
("blenfont", "Font loading and rendering. Used by the user-interface and stamping text into images."),
|
||||
("animrig", "Animation & rigging functionality including key-framing, drivers NLA & actions."),
|
||||
("blenkernel",
|
||||
"Kernel functions "
|
||||
"(data structure manipulation, allocation, free. No interactive tools or UI logic, very low level)."),
|
||||
("blenlib",
|
||||
"Internal misc libraries: "
|
||||
"math functions, lists, random, noise, memory pools, file operations (platform agnostic)."),
|
||||
("blenloader", "Blend file loading and writing as well as in memory undo file system."),
|
||||
("blentranslation", "Internal support for non-English translations (gettext)."),
|
||||
("bmesh", "Mesh-data manipulation. Used by mesh edit-mode."),
|
||||
("compositor", "Image compositor which implements the compositor and various nodes."),
|
||||
("cpucheck", "Support detecting if the CPU is capable of running Blender."),
|
||||
("datatoc", "Utility used by the build-system to convert data-files into source."),
|
||||
("depsgraph",
|
||||
"Dependency graph API. Used to track relations between various pieces of data in a Blender file."),
|
||||
("draw", "Viewport drawing and Eevee."),
|
||||
("editors", "Logic for (expanded on in other sections)."),
|
||||
("freestyle", "Freestyle NPR rendering engine (scene graph, Python code, stroke, geometry.. etc)."),
|
||||
("functions",
|
||||
"Run-time type system that implements lazy functions, multi-function network & generic functions. "
|
||||
"Used by geometry nodes."),
|
||||
("geometry", "Low level geometry functionality. Used by object-modifiers and geometry nodes."),
|
||||
("gpu", "Abstract graphics API's (OpenGL, Metal & Vulkan)."),
|
||||
("ikplugin", "Generic API for inverse-kinematics (IK) solvers. Used by the IK constraint."),
|
||||
("imbuf", "Image buffer API for image manipulation."),
|
||||
("io", "Input/output libraries for various format (including 2D & 3D file formats)."),
|
||||
("makesdna", "Extracting data-definitions stored in the BLEND file-format."),
|
||||
("makesrna", "Defines access method for DNA. Used by the user-interface, Python API & animation system."),
|
||||
("modifiers", "Object modifiers."),
|
||||
("nodes", "Nodes code: `CMP`: composite, `GEO`: geometry, `SHD`: material, `TEX`: texture."),
|
||||
("python", "Python API integration."),
|
||||
("render", "Rendering & baking, integrates various rendering engines, outputs to image & video formats."),
|
||||
("sequencer", "Video sequence editor."),
|
||||
("shader_fx", "Grease pencil effects."),
|
||||
("simulation", "Hair simulation."),
|
||||
],
|
||||
),
|
||||
SectionData(
|
||||
heading="Internal Utilities (maintained internally)",
|
||||
source_code_call_sibling_modules=False,
|
||||
source_code_base="intern/",
|
||||
source_code_dirs=[
|
||||
("atomic", "Low level operations lockless concurrent programming."),
|
||||
("audaspace", "Wrapper (see extern)."),
|
||||
("clog", "C-logging library."),
|
||||
("cycles", "Cycles rendering engine."),
|
||||
("dualcon", "Re-meshing "),
|
||||
("eigen", "Wrapper (see extern/Eigen3/)"),
|
||||
("ghost", "Platform abstraction for windowing and user (mouse, keyboard) input."),
|
||||
("guardedalloc", "Memory allocator used across most of Blender's internal code."),
|
||||
("iksolver", "An IK-solver (useful for character animation)"),
|
||||
("itasc", "An IK-solver (useful for robotics)."),
|
||||
("libc_compat", "C-library compatibility (Linux only)."),
|
||||
("libmv", "Movie clip motion tracking solver."),
|
||||
("mantaflow", "Wrapper (see extern)."),
|
||||
("memutil", "Memory allocation utilities."),
|
||||
("mikktspace", "Calculation for mesh tangents from normals & UV's."),
|
||||
("opencolorio", "Wrapper (see libraries)."),
|
||||
("opensubdiv", "Wrapper (see libraries)."),
|
||||
("openvdb", "OpenVDB wrapper for volumetric data support."),
|
||||
("quadriflow", "Wrapper for quadriflow re-meshing."),
|
||||
("renderdoc_dynload", "Dynamic loader (see libraries)."),
|
||||
("rigidbody", "Wrapper for bullet physics (see extern)."),
|
||||
("sky", "Calculate skylight & solar radiance models."),
|
||||
("slim", "Calculate UV unwrapping, used for the \"Minimum Stretch\" method."),
|
||||
("utfconv", "Unicode text conversion."),
|
||||
("wayland_dynload", "Dynamic loader for wayland (Unix only)."),
|
||||
],
|
||||
),
|
||||
SectionData(
|
||||
heading="External Utilities (maintained externally)",
|
||||
source_code_call_sibling_modules=False,
|
||||
source_code_base="extern/",
|
||||
source_code_dirs=[
|
||||
("audaspace", "A high level audio library for portable audio output."),
|
||||
("binreloc", "Support access binary locations (Linux only)."),
|
||||
("bullet2", "Physics solver with rigid-body support."),
|
||||
("ceres", "A library for modeling and solving large, complicated optimization problems."),
|
||||
("cuew", "Extension wrangler for CUDA."),
|
||||
("curve_fit_nd", "Fit bezier-splines to sampled points. Used for free-hand drawing."),
|
||||
("draco", "Compressed 3D file format support."),
|
||||
("Eigen3", "A C++ template library for linear algebra."),
|
||||
("fast_float", "Fast floating point number parsing."),
|
||||
("fmtlib", "A modern C++ string formatting library."),
|
||||
("gflags", "A library to parse command-line flags. Used by glog."),
|
||||
("glew-es", "Extension wrangler for GLEW-ES."),
|
||||
("glog", "Google C++ logging library. Used by gtest & ceres."),
|
||||
("gmock", "Google C++ mocking library. Used by gtest."),
|
||||
("gtest", "Google C++ testing framework. Used for C & C++ tests."),
|
||||
("hipew", "Wrapper for the HIP C++ library, portability library for AMD & NVIDIA GPUs."),
|
||||
("json", "JSON file format support."),
|
||||
("lzma", "High quality but slower (de)compression library."),
|
||||
("lzo", "Fast (de)compression library."),
|
||||
("mantaflow", "Fluid simulation."),
|
||||
("nanosvg", "Salable vector graphics support."),
|
||||
("quadriflow", "Remeshing that rebuilds the geometry with a more uniform topology."),
|
||||
("rangetree", "Efficient range storage."),
|
||||
("renderdoc", "Graphical GPU debugger."),
|
||||
("tinygltf", "GLTF 3D file format support."),
|
||||
("vulkan_memory_allocator", "Memory allocation utilities for Vulkan GPU back-end."),
|
||||
("wcwidth", "Unicode character width."),
|
||||
("xdnd", "Drag & drop support for X11."),
|
||||
("xxhash", "Fast non-cryptographic hashing functions."),
|
||||
],
|
||||
),
|
||||
SectionData(
|
||||
heading="Pre-compiled Libraries (in SVN, or require install)",
|
||||
source_code_call_sibling_modules=False,
|
||||
source_code_base="lib/{platform}/",
|
||||
source_code_dirs=[
|
||||
("alembic", "An interchangeable computer graphics file format for baked mesh data."),
|
||||
("brotli", "Compression library used to decompress WOFF2 fonts."),
|
||||
("dpcpp", "OneAPI DPC++/C++ Compiler. Used by Cycles oneAPI."),
|
||||
("embree", "A collection of high-performance ray tracing kernels. Used by Cycles."),
|
||||
("epoxy", "Extension wrangler for OpenGL."),
|
||||
("ffmpeg", "Movie encoding/decoding library."),
|
||||
("fftw3", "Fast fourier transformation library."),
|
||||
("freetype", "Font reading & rendering library."),
|
||||
("fribidi",
|
||||
"Bi-directional text support (right-to-left) for Arabic & Hebrew script. "
|
||||
"Intended for complex text shaping (not yet supported)."),
|
||||
("gmp", "Arbitrary precision arithmetic library."),
|
||||
("harfbuzz", "Text shaping engine for for complex script."),
|
||||
("haru", "PDF generation library."),
|
||||
("hiprt", "Ray-tracing for AMD GPU's. Used by Cycles."),
|
||||
("imath", "Library used by OpenEXR image-format."),
|
||||
("jemalloc", "An improved memory allocator."),
|
||||
("jpeg", "JPEG image-format support."),
|
||||
("level-zero", "OneAPI loader & validation. Used by Cycles oneAPI."),
|
||||
("llvm", "Low level virtual machine. Used by OSL."),
|
||||
("materialx", "A standard for representing materials. Used by USD, Hydra & Blender's shader nodes."),
|
||||
("mesa", "Used for it's software OpenGL implementation."),
|
||||
("openal", "Cross platform audio output."),
|
||||
("opencollada", "Support for the COLLADA 3D interchange file format."),
|
||||
("opencolorio", "A solution for highly precise, performant, and consistent color management."),
|
||||
("openexr", "EXR image-format support."),
|
||||
("openimagedenoise", "Denoising filters for images rendered with ray tracing. Used by Cycles."),
|
||||
("openimageio", "A library for reading, writing, and processing images in a wide variety of file formats."),
|
||||
("openjpeg", "JPEG image-format support."),
|
||||
("openpgl", "Intel open path guiding library. Used by Cycles."),
|
||||
("opensubdiv", "Subdivision surface support for meshes."),
|
||||
("openvdb", "Volumetric file-format support."),
|
||||
("osl", "Open Shading Language. Used by Cycles."),
|
||||
("png", "PNG image-format support."),
|
||||
("potrace", "Trace bitmap images to vectors."),
|
||||
("pugixml", "Light-weight C++ XML processing library."),
|
||||
("python", "Python scripting language."),
|
||||
("sdl", "Simple DirectMedia Layer to abstract platform specific code."),
|
||||
("shaderc", "Shader compilation utilities. Used for GLSL shaders to be used with Vulkan."),
|
||||
("sndfile", "Sound encoding/decoding library."),
|
||||
("spnav", "3D mouse support, also known as NDOF. (Unix only)."),
|
||||
("tbb", "Threading Building Blocks (C++ library)."),
|
||||
("tiff", "TIFF image-format support."),
|
||||
("usd", "Universal Scene Description for describing, composing, simulating, & collaborating 3D worlds."),
|
||||
("wayland", "Wayland scanner (Unix only)."),
|
||||
("wayland-protocols", "Wayland protocol definitions (Unix only)."),
|
||||
("wayland_libdecor", "Wayland windowing (Unix only)."),
|
||||
("wayland_weston", "Wayland compositor, used for running headless UI tests (Unix only)."),
|
||||
("webp", "Support for the WEBP image format."),
|
||||
("xml2", "XML encoding / decoding library."),
|
||||
("xr_openxr_sdk", "Virtual reality library."),
|
||||
("zlib", "ZLIB (GZIP) compression library."),
|
||||
("zstd", "Z-standard compression library."),
|
||||
("vulkan", "The Vulkan graphics API. Used by the Vulkan GPU back-end.")
|
||||
],
|
||||
),
|
||||
SectionData(
|
||||
heading="Operating system",
|
||||
source_code_call_sibling_modules=False,
|
||||
source_code_base="",
|
||||
source_code_dirs=[
|
||||
("GPU Driver", "OpenGL (or Metal on Apple)."),
|
||||
("Standard C", "Also known as libc."),
|
||||
("C++ & STL", "C++ Standard Library."),
|
||||
("Windowing", "Cocoa on Apple.\nX11/Wayland on Unix.\nWIN32 on MS-Window."),
|
||||
],
|
||||
),
|
||||
|
||||
SectionData(
|
||||
heading="Other Directories",
|
||||
source_code_call_sibling_modules=False,
|
||||
source_code_base="",
|
||||
source_code_dirs=[
|
||||
("build_files", "Files used by CMake, the build-bot & utilities for packaging blender builds."),
|
||||
("doc", "Scripts for building Blender's Python's API docs, man-page and DOXYGEN documentation."),
|
||||
("locale", "Translations for Blender's interface."),
|
||||
("release", "Files bundled with Blender including fonts, icons, desktop files."),
|
||||
("tests", "Files to run tests & the GIT-LFS test data."),
|
||||
("scripts", "Bundled Python scripts for UI layout, key-map & some operators."),
|
||||
("tools", "Utilities to help with Blender development."),
|
||||
],
|
||||
),
|
||||
|
||||
)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Generic Utilities
|
||||
|
||||
def _function_id() -> str:
|
||||
'''
|
||||
Create a string naming the function n frames up on the stack.
|
||||
'''
|
||||
import sys
|
||||
co = sys._getframe(1).f_code
|
||||
return '{:s}:{:d}:'.format(co.co_name, co.co_firstlineno)
|
||||
|
||||
|
||||
def object_data_materials_setup_default(object_data):
|
||||
object_data.materials.append(MATERIAL_FROM_COLOR["black"])
|
||||
object_data.materials.append(MATERIAL_FROM_COLOR["grey"])
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Internal Utility Classes
|
||||
|
||||
@dataclass
|
||||
class Box2D:
|
||||
min_x: float
|
||||
max_x: float
|
||||
min_y: float
|
||||
max_y: float
|
||||
|
||||
def size(self):
|
||||
return self.max_x - self.min_x, self.max_y - self.min_y
|
||||
|
||||
def union(self, *rest):
|
||||
min_x, max_x, min_y, max_y = self.min_x, self.max_x, self.min_y, self.max_y
|
||||
for other in rest:
|
||||
min_x = min(min_x, other.min_x)
|
||||
max_x = max(max_x, other.max_x)
|
||||
min_y = min(min_y, other.min_y)
|
||||
max_y = max(max_y, other.max_y)
|
||||
return Box2D(min_x=min_x, max_x=max_x, min_y=min_y, max_y=max_y)
|
||||
|
||||
def expanded(self, value):
|
||||
result = self.copy()
|
||||
result.min_x -= value
|
||||
result.max_x += value
|
||||
result.min_y -= value
|
||||
result.max_y += value
|
||||
return result
|
||||
|
||||
def expanded_x(self, value):
|
||||
result = self.copy()
|
||||
result.min_x -= value
|
||||
result.max_x += value
|
||||
return result
|
||||
|
||||
def expanded_y(self, value):
|
||||
result = self.copy()
|
||||
result.min_y -= value
|
||||
result.max_y += value
|
||||
return result
|
||||
|
||||
def copy(self, *, min_x=None, max_x=None, min_y=None, max_y=None):
|
||||
result = self.union()
|
||||
if min_x is not None:
|
||||
result.min_x = min_x
|
||||
if max_x is not None:
|
||||
result.max_x = max_x
|
||||
if min_y is not None:
|
||||
result.min_y = min_y
|
||||
if max_y is not None:
|
||||
result.max_y = max_y
|
||||
return result
|
||||
|
||||
def isect_x(self, other):
|
||||
if other.max_x < self.min_x:
|
||||
return False
|
||||
if self.max_x < other.min_x:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
@dataclass
|
||||
class TextStyle:
|
||||
font: str
|
||||
size: float
|
||||
|
||||
@property
|
||||
def line_height(self):
|
||||
return self.size * 0.01
|
||||
|
||||
def apply(self, txt_data):
|
||||
settings = FONT_STYLE_SETTINGS[self.font]
|
||||
|
||||
scale = 0.01 * settings["scale"]
|
||||
space_line = settings["space_line"]
|
||||
|
||||
txt_data.size = self.size * scale
|
||||
txt_data.space_line = space_line
|
||||
txt_data.font = VFONT_FROM_STYLE[self.font]
|
||||
|
||||
|
||||
text_style_title = TextStyle(font="default", size=20.0)
|
||||
text_style_subheading = TextStyle(font="default", size=14.0)
|
||||
text_style_body = TextStyle(font="default", size=8.0)
|
||||
text_style_dir_title = TextStyle(font="mono", size=8.0)
|
||||
text_style_dir_body = TextStyle(font="default", size=6.0)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Generic Drawing Utilities
|
||||
|
||||
def box_2d_from_vectors(vectors):
|
||||
min_x = max_x = vectors[0][0]
|
||||
min_y = max_y = vectors[0][1]
|
||||
for i in range(1, len(vectors)):
|
||||
x, y = vectors[i][0:2]
|
||||
min_x = min(x, min_x)
|
||||
max_x = max(x, max_x)
|
||||
min_y = min(y, min_y)
|
||||
max_y = max(y, max_y)
|
||||
return Box2D(min_x=min_x, max_x=max_x, min_y=min_y, max_y=max_y)
|
||||
|
||||
|
||||
def box_2d_from_vectors_with_matrix(vectors, matrix):
|
||||
return box_2d_from_vectors([matrix @ Vector(v) for v in vectors])
|
||||
|
||||
|
||||
def draw_text_centered(scene, *, location, text, style):
|
||||
name_gen = _function_id() + repr(text)
|
||||
txt_data = bpy.data.curves.new(name=name_gen, type='FONT')
|
||||
object_data_materials_setup_default(txt_data)
|
||||
|
||||
# Text Object
|
||||
txt_data.body = text
|
||||
style.apply(txt_data)
|
||||
txt_data.align_x = 'CENTER'
|
||||
txt_ob = bpy.data.objects.new(name=name_gen, object_data=txt_data)
|
||||
txt_ob.location.xy = location
|
||||
|
||||
scene.collection.objects.link(txt_ob)
|
||||
|
||||
bpy.context.view_layer.update()
|
||||
return box_2d_from_vectors_with_matrix(txt_ob.bound_box, txt_ob.matrix_world.copy()), txt_ob
|
||||
|
||||
|
||||
def draw_text_left(scene, *, location, text, style):
|
||||
name_gen = _function_id() + repr(text)
|
||||
txt_data = bpy.data.curves.new(name=name_gen, type='FONT')
|
||||
object_data_materials_setup_default(txt_data)
|
||||
|
||||
# Text Object
|
||||
txt_data.body = text
|
||||
style.apply(txt_data)
|
||||
txt_data.align_x = 'LEFT'
|
||||
txt_ob = bpy.data.objects.new(name=name_gen, object_data=txt_data)
|
||||
txt_ob.location.xy = location
|
||||
|
||||
scene.collection.objects.link(txt_ob)
|
||||
|
||||
bpy.context.view_layer.update()
|
||||
return box_2d_from_vectors_with_matrix(txt_ob.bound_box, txt_ob.matrix_world.copy()), txt_ob
|
||||
|
||||
|
||||
def draw_text_left_with_bounds(scene, *, box, text, style):
|
||||
name_gen = _function_id() + repr(text)
|
||||
txt_data = bpy.data.curves.new(name=name_gen, type='FONT')
|
||||
object_data_materials_setup_default(txt_data)
|
||||
|
||||
# Text Object
|
||||
txt_data.body = text
|
||||
style.apply(txt_data)
|
||||
txt_data.align_x = 'LEFT'
|
||||
txt_box = txt_data.text_boxes[0]
|
||||
txt_box.x = box.min_x
|
||||
txt_box.y = box.max_y - style.line_height
|
||||
txt_box.width, txt_box.height = box.size()
|
||||
|
||||
txt_ob = bpy.data.objects.new(name=name_gen, object_data=txt_data)
|
||||
|
||||
scene.collection.objects.link(txt_ob)
|
||||
|
||||
bpy.context.view_layer.update()
|
||||
return box_2d_from_vectors_with_matrix(txt_ob.bound_box, txt_ob.matrix_world.copy()), txt_ob
|
||||
|
||||
|
||||
def draw_arrow(scene, *, location, size, line_width, arrow_data):
|
||||
name_gen = _function_id() + repr(location)
|
||||
|
||||
import bmesh
|
||||
bm = bmesh.new()
|
||||
|
||||
verts = [
|
||||
bm.verts.new(((co[0] * size) + location[0], (co[1] * size) + location[1], 0.0))
|
||||
for co in arrow_data
|
||||
]
|
||||
|
||||
f = bm.faces.new(verts)
|
||||
bm.normal_update()
|
||||
|
||||
bmesh.ops.inset_individual(
|
||||
bm,
|
||||
faces=[f],
|
||||
use_even_offset=True,
|
||||
use_relative_offset=False,
|
||||
thickness=line_width,
|
||||
)
|
||||
|
||||
bm.faces.remove(f)
|
||||
|
||||
me = bpy.data.meshes.new(name=name_gen)
|
||||
object_data_materials_setup_default(me)
|
||||
bm.to_mesh(me)
|
||||
bm.free()
|
||||
|
||||
line_ob = bpy.data.objects.new(name=name_gen, object_data=me)
|
||||
scene.collection.objects.link(line_ob)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Setup
|
||||
|
||||
def setup_page():
|
||||
pass
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Drawing
|
||||
|
||||
def draw_mesh_dashed_line(bm, *, min_x, max_x, y, dash_on, dash_off, line_width, skip_box):
|
||||
x = min_x + (dash_off / 2)
|
||||
line_width_half = line_width / 2
|
||||
while x < max_x:
|
||||
beg_x = x
|
||||
end_x = x + dash_on
|
||||
if not skip_box.isect_x(Box2D(min_x=beg_x, max_x=end_x, min_y=0.0, max_y=0.0)):
|
||||
quad = [bm.verts.new() for i in range(4)]
|
||||
quad[0].co.xy = beg_x, y + line_width_half
|
||||
quad[1].co.xy = beg_x, y - line_width_half
|
||||
quad[2].co.xy = end_x, y - line_width_half
|
||||
quad[3].co.xy = end_x, y + line_width_half
|
||||
bm.faces.new(quad)
|
||||
x += dash_on + dash_off
|
||||
|
||||
|
||||
def draw_mesh_box(bm, *, box, material_index=MATERIAL_INDEX_BLACK):
|
||||
quad = [bm.verts.new() for i in range(4)]
|
||||
quad[0].co.xy = box.min_x, box.min_y
|
||||
quad[1].co.xy = box.max_x, box.min_y
|
||||
quad[2].co.xy = box.max_x, box.max_y
|
||||
quad[3].co.xy = box.min_x, box.max_y
|
||||
f = bm.faces.new(quad)
|
||||
if material_index > 0:
|
||||
f.material_index = material_index
|
||||
|
||||
|
||||
def draw_mesh_box_wire(bm, *, box, line_width):
|
||||
draw_mesh_box(bm, box=box.copy(max_x=box.min_x + line_width))
|
||||
draw_mesh_box(bm, box=box.copy(min_x=box.max_x - line_width))
|
||||
draw_mesh_box(bm, box=box.copy(max_y=box.min_y + line_width))
|
||||
draw_mesh_box(bm, box=box.copy(min_y=box.max_y - line_width))
|
||||
|
||||
|
||||
def draw_mesh_box_drop_shadow(bm, *, box, size):
|
||||
# Right hand side.
|
||||
draw_mesh_box(
|
||||
bm,
|
||||
box=box.copy(
|
||||
min_x=box.max_x,
|
||||
max_x=box.max_x + size,
|
||||
min_y=box.min_y - size,
|
||||
max_y=box.max_y - size,
|
||||
),
|
||||
material_index=MATERIAL_INDEX_GREY,
|
||||
)
|
||||
# Bottom.
|
||||
draw_mesh_box(
|
||||
bm,
|
||||
box=box.copy(
|
||||
min_x=box.min_x + size,
|
||||
max_x=box.max_x + size,
|
||||
min_y=box.min_y - size,
|
||||
max_y=box.min_y,
|
||||
),
|
||||
material_index=MATERIAL_INDEX_GREY,
|
||||
)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Drawing
|
||||
|
||||
def draw_centered_title(scene, step_y, text):
|
||||
box, ob = draw_text_centered(scene, location=(0.0, step_y), text=text, style=text_style_title)
|
||||
return box, ob
|
||||
|
||||
|
||||
def sub_section_line(scene, box, step_y):
|
||||
name_gen = _function_id() + repr(box)
|
||||
|
||||
import bmesh
|
||||
|
||||
bm = bmesh.new()
|
||||
|
||||
arrow_h = 0.075
|
||||
arrow_w = 0.1
|
||||
|
||||
for sign in (-1.0, 1.0):
|
||||
# Left triangle.
|
||||
vs = [bm.verts.new() for i in range(3)]
|
||||
l = PAGE_WIDTH_HALF * sign
|
||||
vs[0].co.xy = l, step_y
|
||||
vs[1].co.xy = l - (0.1 * sign), step_y - (arrow_h / 2)
|
||||
vs[2].co.xy = l - (0.1 * sign), step_y + (arrow_h / 2)
|
||||
bm.faces.new(vs)
|
||||
|
||||
# Horizontal line.
|
||||
draw_mesh_dashed_line(
|
||||
bm,
|
||||
min_x=-(PAGE_WIDTH_HALF - arrow_w),
|
||||
max_x=+(PAGE_WIDTH_HALF - arrow_w),
|
||||
y=step_y,
|
||||
dash_on=0.0375,
|
||||
dash_off=0.0375,
|
||||
line_width=0.015,
|
||||
skip_box=box.expanded_x(0.0375),
|
||||
)
|
||||
|
||||
me = bpy.data.meshes.new(name=name_gen)
|
||||
object_data_materials_setup_default(me)
|
||||
bm.to_mesh(me)
|
||||
bm.free()
|
||||
|
||||
line_ob = bpy.data.objects.new(name=name_gen, object_data=me)
|
||||
scene.collection.objects.link(line_ob)
|
||||
|
||||
|
||||
def draw_directories(scene, box_body, source_code_dirs):
|
||||
name_gen = _function_id() + repr(source_code_dirs)
|
||||
|
||||
import bmesh
|
||||
|
||||
box_result = box_body.copy()
|
||||
|
||||
box_line_width = 0.01
|
||||
box_size = 0.75, 0.3
|
||||
box_gap = 0.075, 0.075
|
||||
box_inner_margin = 0.04
|
||||
bm = bmesh.new()
|
||||
box_dropshadow_size = 0.025
|
||||
|
||||
me = bpy.data.meshes.new(name=name_gen)
|
||||
|
||||
step_x = box_body.min_x
|
||||
step_y = box_body.min_y
|
||||
|
||||
# Adjust the box height based on the bounds of the body-text
|
||||
# (requires postponing drawing the boxes so the size is known).
|
||||
USE_FLEXIBLE_SIZE_X = True
|
||||
# Use double-width boxes when the directory name doesn't fit.
|
||||
USE_FLEXIBLE_SIZE_Y = True
|
||||
|
||||
boxes_pending = []
|
||||
|
||||
def draw_directory_boxes_pending():
|
||||
if not boxes_pending:
|
||||
return None
|
||||
y = boxes_pending[0][1].min_y
|
||||
for _, box_text in boxes_pending:
|
||||
y = min(y, box_text.min_y)
|
||||
y -= box_inner_margin
|
||||
|
||||
for box_draw, _ in boxes_pending:
|
||||
box = box_draw.copy(min_y=y)
|
||||
draw_mesh_box_wire(bm, box=box, line_width=box_line_width)
|
||||
draw_mesh_box_drop_shadow(bm, box=box, size=box_dropshadow_size)
|
||||
|
||||
bounds = boxes_pending[0][0].union(*[box_draw for box_draw, _ in boxes_pending])
|
||||
bounds.min_y = y
|
||||
|
||||
boxes_pending.clear()
|
||||
return bounds
|
||||
|
||||
for dir_name, description in source_code_dirs:
|
||||
box_size_flex = box_size
|
||||
|
||||
# Draw text.
|
||||
box_dir, box_dir_ob = draw_text_left(
|
||||
scene,
|
||||
# First get the bounds.
|
||||
location=(0.0, 0.0),
|
||||
text=dir_name,
|
||||
style=text_style_dir_title,
|
||||
)
|
||||
if USE_FLEXIBLE_SIZE_X:
|
||||
if box_size_flex[0] < (box_dir.size()[0] + box_inner_margin * 2.0):
|
||||
box_size_flex = box_size_flex[0] + box_gap[0] + box_size[0], box_size[1]
|
||||
|
||||
if step_x + box_size_flex[0] + box_gap[0] > box_body.max_x:
|
||||
step_y_size = box_size_flex[1]
|
||||
if USE_FLEXIBLE_SIZE_Y:
|
||||
box_pending = draw_directory_boxes_pending()
|
||||
if box_pending is not None:
|
||||
step_y_size = box_pending.size()[1]
|
||||
step_y -= step_y_size + box_gap[1]
|
||||
step_x = box_body.min_x
|
||||
|
||||
box_draw = Box2D(min_x=step_x, max_x=step_x + box_size_flex[0], min_y=step_y - box_size_flex[1], max_y=step_y)
|
||||
if not USE_FLEXIBLE_SIZE_Y:
|
||||
draw_mesh_box_wire(bm, box=box_draw, line_width=box_line_width)
|
||||
draw_mesh_box_drop_shadow(bm, box=box_draw, size=box_dropshadow_size)
|
||||
|
||||
next_y = step_y - ((text_style_dir_title.line_height * 0.75) + box_inner_margin)
|
||||
next_y_body_begin = step_y - (text_style_dir_title.line_height + box_inner_margin)
|
||||
|
||||
box_dir_ob.location.xy = (step_x + box_inner_margin, next_y)
|
||||
|
||||
if description:
|
||||
box_text_bounds, _ = draw_text_left_with_bounds(
|
||||
scene,
|
||||
box=Box2D(
|
||||
min_x=box_draw.min_x + box_inner_margin,
|
||||
max_x=box_draw.max_x - box_inner_margin,
|
||||
min_y=box_draw.min_y,
|
||||
max_y=next_y_body_begin,
|
||||
),
|
||||
text=description,
|
||||
style=text_style_dir_body,
|
||||
)
|
||||
if USE_FLEXIBLE_SIZE_Y:
|
||||
if description:
|
||||
boxes_pending.append((box_draw, box_text_bounds))
|
||||
else:
|
||||
boxes_pending.append((box_draw, box_draw.copy(min_y=next_y_body_begin)))
|
||||
|
||||
step_x += box_size_flex[0] + box_gap[0]
|
||||
|
||||
step_y_size = box_size[1]
|
||||
if USE_FLEXIBLE_SIZE_Y:
|
||||
box_pending = draw_directory_boxes_pending()
|
||||
if box_pending is not None:
|
||||
step_y_size = box_pending.size()[1]
|
||||
|
||||
box_result.min_y = step_y - (step_y_size + box_gap[1])
|
||||
|
||||
object_data_materials_setup_default(me)
|
||||
bm.to_mesh(me)
|
||||
bm.free()
|
||||
|
||||
line_ob = bpy.data.objects.new(name=name_gen, object_data=me)
|
||||
scene.collection.objects.link(line_ob)
|
||||
|
||||
return box_result
|
||||
|
||||
|
||||
def draw_legend(scene, *, step_y, legend_data, text):
|
||||
|
||||
unit = PAGE_WIDTH / 16.0
|
||||
|
||||
x_left = -PAGE_WIDTH_HALF + (unit * 3)
|
||||
|
||||
draw_arrow(
|
||||
scene,
|
||||
location=(x_left, step_y - 0.25),
|
||||
size=0.0575,
|
||||
line_width=0.0075,
|
||||
arrow_data=legend_data,
|
||||
)
|
||||
|
||||
# Draw text.
|
||||
box_dir, box_dir_ob = draw_text_left(
|
||||
scene,
|
||||
# First get the bounds.
|
||||
location=(x_left + 0.25, step_y - 0.175),
|
||||
text=text,
|
||||
style=text_style_subheading,
|
||||
)
|
||||
|
||||
box_body = Box2D(
|
||||
min_x=-PAGE_WIDTH_HALF + unit * 3,
|
||||
max_x=box_dir.max_x,
|
||||
min_y=step_y - 0.25,
|
||||
max_y=step_y,
|
||||
)
|
||||
|
||||
return box_body
|
||||
|
||||
|
||||
def draw_sub_section(scene, *, step_y, section_data, is_last):
|
||||
|
||||
box_sub_heading, _ = draw_text_centered(
|
||||
scene,
|
||||
# The 0.025 tweak is needed to be vertically centered.
|
||||
# This depends on the exact font used.
|
||||
location=(0.0, step_y + 0.025),
|
||||
text=section_data.heading,
|
||||
style=text_style_subheading,
|
||||
)
|
||||
|
||||
# Add dashes and arrows around.
|
||||
sub_section_line(scene, box_sub_heading, step_y + 0.05)
|
||||
|
||||
step_y -= 0.2
|
||||
|
||||
box_dir, _ = draw_text_left(
|
||||
scene,
|
||||
location=(-PAGE_WIDTH_HALF, step_y),
|
||||
text=section_data.source_code_base,
|
||||
style=text_style_body,
|
||||
)
|
||||
|
||||
step_y += 0.1
|
||||
|
||||
# Box with no height to draw mini boxes into.
|
||||
unit = PAGE_WIDTH / 16.0
|
||||
box_body = Box2D(
|
||||
min_x=-PAGE_WIDTH_HALF + unit * 3,
|
||||
max_x=PAGE_WIDTH_HALF - unit,
|
||||
min_y=step_y,
|
||||
max_y=step_y,
|
||||
)
|
||||
|
||||
box_dirs = draw_directories(scene, box_body, section_data.source_code_dirs)
|
||||
|
||||
# Draw an arrow on the right hand side.
|
||||
if not is_last:
|
||||
draw_arrow(
|
||||
scene,
|
||||
location=(PAGE_WIDTH_HALF - 0.175, step_y - 0.25),
|
||||
size=0.0575,
|
||||
line_width=0.0075,
|
||||
arrow_data=(
|
||||
ARROW_DOWN_AND_SIDEWAYS if section_data.source_code_call_sibling_modules else
|
||||
ARROW_DOWN
|
||||
),
|
||||
)
|
||||
|
||||
return box_sub_heading.union(box_dir, box_body, box_dirs)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Render Output
|
||||
|
||||
def render_output(scene, bounds, filepath):
|
||||
name_gen = _function_id()
|
||||
|
||||
scene.render.filepath = filepath
|
||||
|
||||
world = bpy.data.worlds.new(name_gen)
|
||||
world.color = 1.0, 1.0, 1.0
|
||||
world.use_nodes = False
|
||||
scene.world = world
|
||||
|
||||
# Some space around the edges.
|
||||
bounds_size = bounds.expanded(0.15).size()
|
||||
|
||||
camera_data = bpy.data.cameras.new(name_gen)
|
||||
camera_data.type = 'ORTHO'
|
||||
camera_data.ortho_scale = max(bounds_size)
|
||||
|
||||
camera = bpy.data.objects.new(name_gen, camera_data)
|
||||
camera.rotation_euler = Euler((0.0, 0.0, 0.0), 'XYZ')
|
||||
|
||||
scene.camera = camera
|
||||
camera.location = (0.0, (bounds.min_y + bounds.max_y) / 2.0, 10.0)
|
||||
scene.collection.objects.link(camera)
|
||||
|
||||
render = scene.render
|
||||
render.image_settings.file_format = 'JPEG'
|
||||
render.image_settings.color_depth = '8'
|
||||
render.image_settings.color_mode = 'RGB'
|
||||
render.image_settings.quality = 75
|
||||
render.use_file_extension = False
|
||||
render.resolution_x = RESOLUTION_X
|
||||
render.resolution_y = int(RESOLUTION_X * (bounds_size[1] / bounds_size[0]))
|
||||
render.resolution_percentage = int(100 * RESOLUTION_SCALE)
|
||||
|
||||
bpy.context.view_layer.update()
|
||||
|
||||
if OUTPUT_BLEND_FILE:
|
||||
bpy.ops.wm.save_mainfile('EXEC_DEFAULT', filepath=os.path.splitext(filepath)[0] + ".blend")
|
||||
|
||||
if OUTPUT_IMAGE_FILE:
|
||||
print("Rendering:", filepath)
|
||||
bpy.ops.render.render(write_still=True)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Validate Sections
|
||||
|
||||
|
||||
def validate_sections():
|
||||
sections_shared = {section.source_code_base: [] for section in SECTIONS}
|
||||
|
||||
for section in SECTIONS:
|
||||
if section.heading == "Operating system":
|
||||
continue
|
||||
sections_shared[section.source_code_base].append(section)
|
||||
|
||||
# Consider the parent directories "documented".
|
||||
# Because their children are included.
|
||||
dirs_parent_documented = set()
|
||||
for dirpath in sections_shared.keys():
|
||||
if dirpath == "":
|
||||
continue
|
||||
if not dirpath.endswith("/"):
|
||||
raise Exception("Directories must end with a \"/\", found {:s}".format(dirpath))
|
||||
dirpath_split = dirpath.strip("/").split("/")
|
||||
for i in range(1, len(dirpath_split) + 1):
|
||||
dirpath = "/".join(dirpath_split[0:i])
|
||||
dirs_parent_documented.add(dirpath)
|
||||
|
||||
this_platform = ""
|
||||
for e in os.scandir(os.path.join(ROOT_DIR, "lib")):
|
||||
dirpath_full = os.path.join(ROOT_DIR, "lib", e.name)
|
||||
if os.path.isdir(os.path.join(dirpath_full, "jpeg")):
|
||||
this_platform = e.name
|
||||
break
|
||||
|
||||
dirs_fs_ignore = {
|
||||
".git",
|
||||
".github",
|
||||
".gitea",
|
||||
}
|
||||
|
||||
for source_code_base, sections in sections_shared.items():
|
||||
|
||||
if "{platform}" in source_code_base:
|
||||
source_code_base = source_code_base.replace("{platform}", this_platform)
|
||||
|
||||
dirs_fs = set([
|
||||
e.name for e in os.scandir(os.path.join(ROOT_DIR, source_code_base))
|
||||
if e.is_dir()
|
||||
])
|
||||
|
||||
for section in sections:
|
||||
for dir_docs, _ in section.source_code_dirs:
|
||||
if dir_docs in dirs_fs:
|
||||
dirs_fs.remove(dir_docs)
|
||||
continue
|
||||
|
||||
print("Directory no longer exists:", os.path.join(source_code_base, dir_docs))
|
||||
|
||||
for dir_fs in sorted(dirs_fs):
|
||||
if dir_fs in dirs_fs_ignore:
|
||||
continue
|
||||
if dir_fs in dirs_parent_documented:
|
||||
continue
|
||||
print("Directory has no docs:", os.path.join(source_code_base, dir_fs))
|
||||
|
||||
|
||||
def argparse_create():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=__doc__,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--only-validate",
|
||||
dest="only_validate",
|
||||
action='store_true',
|
||||
help="Validate the directory listing and exit.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output",
|
||||
dest="output",
|
||||
default="code_layout.jpg",
|
||||
type=str,
|
||||
help="The output path to write the JPEG to.",
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Main
|
||||
|
||||
def main():
|
||||
args = argparse_create().parse_args((sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []))
|
||||
|
||||
# Always validate as it's cheap and notifies of incomplete docs.
|
||||
validate_sections()
|
||||
if args.only_validate:
|
||||
print("Validation complete, exiting!")
|
||||
return
|
||||
|
||||
bpy.ops.wm.read_homefile(use_empty=True, use_factory_startup=True)
|
||||
|
||||
# Setup materials.
|
||||
material = bpy.data.materials.new("Flat Black")
|
||||
material.use_nodes = False
|
||||
material.specular_intensity = 0.0
|
||||
material.diffuse_color = (0.0, 0.0, 0.0, 1.0)
|
||||
MATERIAL_FROM_COLOR["black"] = material
|
||||
del material
|
||||
material = bpy.data.materials.new("Flat Grey")
|
||||
material.use_nodes = False
|
||||
material.specular_intensity = 0.0
|
||||
material.diffuse_color = (0.4, 0.4, 0.4, 1.0)
|
||||
MATERIAL_FROM_COLOR["grey"] = material
|
||||
del material
|
||||
|
||||
# Setup fonts.
|
||||
VFONT_FROM_STYLE["default"] = bpy.data.fonts.load(FONT_FILE_DEFAULT)
|
||||
VFONT_FROM_STYLE["mono"] = bpy.data.fonts.load(FONT_FILE_MONO)
|
||||
|
||||
scene = bpy.context.scene
|
||||
scene.render.engine = 'BLENDER_EEVEE_NEXT'
|
||||
|
||||
# Without this, the whites are grey.
|
||||
scene.view_settings.view_transform = "Standard"
|
||||
|
||||
sub_section_gap_y = 0.1
|
||||
|
||||
setup_page()
|
||||
step_y = 0.0
|
||||
box, _ = draw_centered_title(scene, step_y, "Blender Code Layout")
|
||||
bounds_max_y = box.max_y
|
||||
|
||||
step_y -= box.max_y - box.min_y
|
||||
|
||||
# Draw the legends for each kind of arrow.
|
||||
box = draw_legend(
|
||||
scene,
|
||||
step_y=step_y,
|
||||
legend_data=ARROW_DOWN,
|
||||
text="Modules only call lower level code.",
|
||||
)
|
||||
step_y = box.min_y - 0.175
|
||||
box = draw_legend(
|
||||
scene,
|
||||
step_y=step_y,
|
||||
legend_data=ARROW_DOWN_AND_SIDEWAYS,
|
||||
text="Modules call each other and lower level code.",
|
||||
)
|
||||
step_y = box.min_y - (0.175 + 0.15)
|
||||
|
||||
# Draw the sections.
|
||||
for section_data in SECTIONS:
|
||||
box = draw_sub_section(
|
||||
scene,
|
||||
step_y=step_y,
|
||||
section_data=section_data,
|
||||
is_last=section_data is SECTIONS[-1],
|
||||
)
|
||||
step_y = box.min_y - sub_section_gap_y
|
||||
|
||||
# Setup Y resolutions.
|
||||
bounds = Box2D(min_x=-PAGE_WIDTH_HALF, max_x=PAGE_WIDTH_HALF, min_y=box.min_y, max_y=bounds_max_y)
|
||||
|
||||
print(args.output)
|
||||
render_output(scene, bounds, args.output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user