using System; using System.Collections.Generic; using Godot; namespace Cthangover.Live2D.Cubism.Framework { /// /// Manages the 2D transformation (scale, translation, alignment) for a /// Cubism model rendered inside a Godot . /// /// Works with two coordinate systems: /// /// Cubism canvas coordinates (the model's native coordinate space) /// Godot node local coordinates (where the model is placed) /// /// /// The scale is computed relative to the canvas dimensions stored at construction. /// and set a uniform scale based /// on the ratio of target size to canvas size. reads /// positioning keys from a Cubism .model3.json "Layout" section. /// writes the final transform to a Godot Node2D. /// public class CubismModelMatrix { private float _width; private float _height; private float _scaleX = 1f; private float _scaleY = 1f; private float _translateX; private float _translateY; /// Creates an identity matrix with no known canvas size. public CubismModelMatrix() { } /// /// Creates a matrix referenced to the given canvas pixel dimensions. /// Subsequent calls to and /// will compute scale as target / canvas. /// public CubismModelMatrix(float width, float height) { _width = width; _height = height; } /// Current uniform scale on the X axis. public float ScaleX => _scaleX; /// Current uniform scale on the Y axis. public float ScaleY => _scaleY; /// Current X translation offset in Godot local coordinates. public float TranslateX => _translateX; /// Current Y translation offset in Godot local coordinates. public float TranslateY => _translateY; /// /// Sets uniform scale to match the given width. Scale = targetWidth / canvasWidth. /// Does nothing if canvas width is zero or unknown. /// public void SetWidth(float w) { if (_width > 0f) { var scale = w / _width; _scaleX = scale; _scaleY = scale; } } /// /// Sets uniform scale to match the given height. Scale = targetHeight / canvasHeight. /// Does nothing if canvas height is zero or unknown. /// public void SetHeight(float h) { if (_height > 0f) { var scale = h / _height; _scaleX = scale; _scaleY = scale; } } /// Sets absolute translation position. public void SetPosition(float x, float y) { _translateX = x; _translateY = y; } /// Sets absolute X translation. public void SetX(float x) => _translateX = x; /// Sets absolute Y translation. public void SetY(float y) => _translateY = y; /// /// Centers the model horizontally and vertically at the given point. /// Equivalent to calling then . /// public void SetCenterPosition(float x, float y) { CenterX(x); CenterY(y); } /// /// Centers horizontally: translateX = x - (width * scaleX) / 2. /// public void CenterX(float x) => _translateX = x - (_width * _scaleX) / 2f; /// /// Centers vertically: translateY = y - (height * scaleY) / 2. /// public void CenterY(float y) => _translateY = y - (_height * _scaleY) / 2f; /// Aligns the top edge of the model to y. public void Top(float y) => _translateY = y; /// Aligns the bottom edge of the model to y. public void Bottom(float y) => _translateY = y - _height * _scaleY; /// Aligns the left edge of the model to x. public void Left(float x) => _translateX = x; /// Aligns the right edge of the model to x. public void Right(float x) => _translateX = x - _width * _scaleX; /// /// Configures scale and position from a layout dictionary. /// Supported keys: "width", "height", "x", "y", "center_x", "center_y", /// "top", "bottom", "left", "right". /// Width/height are applied first (since they affect scale), /// then position keys are applied. /// public void SetupFromLayout(Dictionary layout) { foreach (var kvp in layout) { switch (kvp.Key) { case "width": SetWidth(kvp.Value); break; case "height": SetHeight(kvp.Value); break; } } foreach (var kvp in layout) { switch (kvp.Key) { case "x": SetX(kvp.Value); break; case "y": SetY(kvp.Value); break; case "center_x": CenterX(kvp.Value); break; case "center_y": CenterY(kvp.Value); break; case "top": Top(kvp.Value); break; case "bottom": Bottom(kvp.Value); break; case "left": Left(kvp.Value); break; case "right": Right(kvp.Value); break; } } } /// /// Writes the current scale and translation to a Godot . /// Sets both Scale and Position properties. /// public void ApplyToNode(Node2D node) { node.Scale = new Vector2(_scaleX, _scaleY); node.Position = new Vector2(_translateX, _translateY); } } }