优化图片对比速度
This commit is contained in:
parent
2931b60ab5
commit
0c235c839a
Binary file not shown.
@ -1,3 +0,0 @@
|
||||
call main.exe -src image -target image_2 -distance_difference 0.06
|
||||
|
||||
pause
|
@ -1,4 +1,5 @@
|
||||
#if UNITY_EDITOR
|
||||
using Sirenix.OdinInspector;
|
||||
using System;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
@ -8,8 +9,10 @@ namespace UguiToolkit.Editor
|
||||
{
|
||||
public interface IEntity
|
||||
{
|
||||
void SetOriginalMatrix(Transform tf);
|
||||
void ApplyTransform(Transform tf);
|
||||
void ApplyTransformByParent(Transform parentTf);
|
||||
void InternalApplyTransform(Transform tf);
|
||||
void ApplyTransformByParent(Transform parentTf, Transform tf);
|
||||
bool IsInside(Transform tf);
|
||||
void ApplyData<T>(T ui) where T: Component;
|
||||
void InitPreview();
|
||||
@ -23,6 +26,8 @@ namespace UguiToolkit.Editor
|
||||
// ElementInfo
|
||||
private T2 m_elementInfo;
|
||||
private UnityEngine.UI.Image m_selectionImg;
|
||||
private Matrix4x4 lastMoveMatrix;
|
||||
private Matrix4x4 oldTransformMatrix;
|
||||
|
||||
public T2 ElementInfo => m_elementInfo;
|
||||
public void ShowSelectionImg(bool show)
|
||||
@ -33,13 +38,45 @@ namespace UguiToolkit.Editor
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public abstract void ApplyTransform(Transform tf);
|
||||
public abstract void InitPreview();
|
||||
protected abstract void OnApplyTransform(Transform tf);
|
||||
protected abstract void OnApplyData(T1 ui);
|
||||
|
||||
public virtual void ApplyTransformByParent(Transform parentTf) { }
|
||||
public virtual void ApplyTransformByParent(Transform parentTf, Transform tf)
|
||||
{
|
||||
var position = lastMoveMatrix.GetColumn(3); // 提取位置
|
||||
var rotation = Quaternion.LookRotation(lastMoveMatrix.GetColumn(2), lastMoveMatrix.GetColumn(1)); // 提取旋转
|
||||
Debug.Log($"[I] ApplyTransformByParent {position}, {rotation.eulerAngles}");
|
||||
parentTf.position += new Vector3(position.x, position.y, position.z);
|
||||
parentTf.rotation *= rotation;
|
||||
|
||||
if (tf)
|
||||
{
|
||||
var lastTransformMatrixInverse = lastMoveMatrix.inverse;
|
||||
position = lastTransformMatrixInverse.GetColumn(3); // 提取位置
|
||||
rotation = Quaternion.LookRotation(lastTransformMatrixInverse.GetColumn(2), lastTransformMatrixInverse.GetColumn(1)); // 提取旋转
|
||||
tf.position += new Vector3(position.x, position.y, position.z);
|
||||
tf.rotation *= rotation;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetOriginalMatrix(Transform tf)
|
||||
{
|
||||
oldTransformMatrix = Matrix4x4.TRS(tf.position, tf.rotation, tf.localScale);
|
||||
}
|
||||
|
||||
public void InternalApplyTransform(Transform tf)
|
||||
{
|
||||
OnApplyTransform(tf);
|
||||
}
|
||||
|
||||
public void ApplyTransform(Transform tf)
|
||||
{
|
||||
Debug.Log($"[I] ApplyTransform {tf.name}");
|
||||
OnApplyTransform(tf);
|
||||
Matrix4x4 newTransformMatrix = Matrix4x4.TRS(tf.position, tf.rotation, tf.localScale);
|
||||
lastMoveMatrix = newTransformMatrix * oldTransformMatrix.inverse;
|
||||
}
|
||||
|
||||
public bool IsInside(Transform tf)
|
||||
{
|
||||
@ -74,7 +111,7 @@ namespace UguiToolkit.Editor
|
||||
rtf.sizeDelta = new Vector2(m_elementInfo.w, m_elementInfo.h);
|
||||
|
||||
m_selectionImg = go.AddComponent<UnityEngine.UI.Image>();
|
||||
m_selectionImg.color = new Color(0, 1, 0, 0.3f);
|
||||
m_selectionImg.color = new Color(0, 1, 0, 0.4f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ namespace UguiToolkit.Editor
|
||||
[ShowInInspector]
|
||||
private Matrix4x4 lastTransformMatrix;
|
||||
|
||||
|
||||
private Image m_previewImage;
|
||||
|
||||
// ²éÕÒʱµ÷ÓÃ
|
||||
@ -34,14 +35,6 @@ namespace UguiToolkit.Editor
|
||||
this.needFillTransform = true;
|
||||
}
|
||||
|
||||
public override void ApplyTransformByParent(Transform parentTf)
|
||||
{
|
||||
var position = lastTransformMatrix.GetColumn(3); // ÌáȡλÖÃ
|
||||
var rotation = Quaternion.LookRotation(lastTransformMatrix.GetColumn(2), lastTransformMatrix.GetColumn(1)); // ÌáÈ¡Ðýת
|
||||
parentTf.position += new Vector3(position.x, position.y, position.z);
|
||||
parentTf.rotation *= rotation;
|
||||
}
|
||||
|
||||
private void LoadImageFromFile(string path)
|
||||
{
|
||||
if (System.IO.File.Exists(path))
|
||||
@ -83,14 +76,11 @@ namespace UguiToolkit.Editor
|
||||
}
|
||||
|
||||
|
||||
public override void ApplyTransform(Transform tf)
|
||||
protected override void OnApplyTransform(Transform tf)
|
||||
{
|
||||
var rt = tf as RectTransform;
|
||||
if (needFillTransform)
|
||||
{
|
||||
Matrix4x4 oldTransformMatrix = Matrix4x4.TRS(tf.position, tf.rotation, tf.localScale);
|
||||
|
||||
|
||||
var pos = ElementInfo.Position;
|
||||
var worldPos = StageManager.Instance.PrefabContentsRoot.transform.TransformPoint(new Vector3(pos.x, pos.y, 0));
|
||||
var anchorMin = rt.anchorMin;
|
||||
@ -118,6 +108,7 @@ namespace UguiToolkit.Editor
|
||||
// µ÷Õû´óС
|
||||
rt.anchorMin = rt.pivot;
|
||||
rt.anchorMax = rt.pivot;
|
||||
var oldSizeDelta = rt.sizeDelta;
|
||||
rt.sizeDelta = new Vector2(size.x, size.y);
|
||||
rt.anchorMin = anchorMin;
|
||||
rt.anchorMax = anchorMax;
|
||||
@ -131,10 +122,6 @@ namespace UguiToolkit.Editor
|
||||
rt.pivot = oldPiovt;
|
||||
var offsetRectPos = oldRect.position - rt.rect.position;
|
||||
rt.Translate(offsetRectPos);
|
||||
|
||||
|
||||
Matrix4x4 newTransformMatrix = Matrix4x4.TRS(tf.position, tf.rotation, tf.localScale);
|
||||
lastTransformMatrix = newTransformMatrix * oldTransformMatrix.inverse;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -13,7 +13,7 @@ namespace UguiToolkit.Editor
|
||||
{
|
||||
public class PrefabEntity : BaseEntity<Transform, LayoutInfo.PrefabInfo>
|
||||
{
|
||||
public override void ApplyTransform(Transform tf)
|
||||
protected override void OnApplyTransform(Transform tf)
|
||||
{
|
||||
var position = ElementInfo.Position;
|
||||
tf.position = StageManager.Instance.PrefabContentsRoot.transform.TransformPoint(new Vector3(position.x, position.y, 0));
|
||||
@ -22,10 +22,9 @@ namespace UguiToolkit.Editor
|
||||
public override void InitPreview()
|
||||
{
|
||||
// ´´½¨Ô¤ÖÆÌå
|
||||
var setting = GlobalManager.Instance.setting;
|
||||
var asset = GetPrefabAsset(setting.commonPrefabForUIDirPath);
|
||||
if (asset != null) {
|
||||
var go = GameObject.Instantiate(asset, transform);
|
||||
var asset = GetPrefabAsset(GetCommonDirPath());
|
||||
if (asset) {
|
||||
var go = PrefabUtility.InstantiatePrefab(asset, transform) as GameObject;
|
||||
EntityHelper.UpdateHierarchyAndSceneVisibilityOfEntity(false, go);
|
||||
|
||||
go.name = asset.name;
|
||||
@ -43,12 +42,7 @@ namespace UguiToolkit.Editor
|
||||
ApplyTransform(ui);
|
||||
}
|
||||
|
||||
public GameObject CopyPrefabGo()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
private GameObject GetPrefabAsset(string commonDirPath)
|
||||
public GameObject GetPrefabAsset(string commonDirPath)
|
||||
{
|
||||
var elementInfo = ElementInfo;
|
||||
var guids = AssetDatabase.FindAssets(elementInfo.prefabName, new string[] { commonDirPath });
|
||||
@ -73,6 +67,12 @@ namespace UguiToolkit.Editor
|
||||
var setting = GlobalManager.Instance.setting;
|
||||
return PrefabUtility.IsAnyPrefabInstanceRoot(asset) && PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(asset).StartsWith(setting.commonPrefabForUIDirPath);
|
||||
}
|
||||
|
||||
public static string GetCommonDirPath()
|
||||
{
|
||||
var setting = GlobalManager.Instance.setting;
|
||||
return setting.commonPrefabForUIDirPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ namespace UguiToolkit.Editor
|
||||
ApplyTransform(transform);
|
||||
}
|
||||
|
||||
public override void ApplyTransform(Transform tf)
|
||||
protected override void OnApplyTransform(Transform tf)
|
||||
{
|
||||
var position = ElementInfo.Position;
|
||||
tf.position = StageManager.Instance.PrefabContentsRoot.transform.TransformPoint(new Vector3(position.x, position.y, 0));
|
||||
|
@ -13,7 +13,7 @@ namespace UguiToolkit.Editor
|
||||
{
|
||||
private TextMeshProUGUI m_previewText;
|
||||
|
||||
public override void ApplyTransform(Transform tf)
|
||||
protected override void OnApplyTransform(Transform tf)
|
||||
{
|
||||
var rt = tf as RectTransform;
|
||||
|
||||
@ -37,11 +37,6 @@ namespace UguiToolkit.Editor
|
||||
|
||||
protected override void OnApplyData(TextMeshProUGUI ui)
|
||||
{
|
||||
ui.text = ElementInfo.text;
|
||||
ui.fontSize = (int)ElementInfo.size;
|
||||
ui.color = ElementInfo.color;
|
||||
ui.alignment = TextAlignmentOptions.Center;
|
||||
|
||||
SetTMPByTextInfo(ui, ElementInfo);
|
||||
}
|
||||
|
||||
@ -60,9 +55,15 @@ namespace UguiToolkit.Editor
|
||||
|
||||
private static void SetTMPByTextInfo(TextMeshProUGUI ui, LayoutInfo.TextInfo textInfo)
|
||||
{
|
||||
// TODO: 自行扩展
|
||||
|
||||
float2 sizeOffset = float2.zero;
|
||||
// TODO: 自行扩展
|
||||
|
||||
if (!ui.hasStringID) ui.text = textInfo.text;
|
||||
ui.fontSize = (int)textInfo.size;
|
||||
ui.color = textInfo.color;
|
||||
ui.alignment = TextAlignmentOptions.Center;
|
||||
|
||||
if (GetTextFontPreset(textInfo, out var textFontPreset))
|
||||
{
|
||||
ui.font = textFontPreset.tmpAsset;
|
||||
|
@ -1,92 +0,0 @@
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEngine;
|
||||
using System.IO;
|
||||
using Newtonsoft.Json;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
|
||||
namespace UguiToolkit.Editor
|
||||
{
|
||||
public static class CommandHelper
|
||||
{
|
||||
public static void CalcRotScale(string srcImgDirPath, string targetImgDirPath, float distanceDifference, Action<RotScaleJsonData> callback)
|
||||
{
|
||||
var rotScaleInfoFilePath = Path.GetFullPath(EditorConst.RotScaleInfoFilePath);
|
||||
var rotScaleInfoToolFilePath = Path.GetFullPath(EditorConst.RotScaleInfoToolFilePath);
|
||||
if (File.Exists(rotScaleInfoFilePath)) File.Delete(rotScaleInfoFilePath);
|
||||
|
||||
var cmd = string.Format("{0} -src {1} -target {2} -distance_difference {3} -output_path {4} -filter __background__.png",
|
||||
rotScaleInfoToolFilePath,
|
||||
Path.GetFullPath(srcImgDirPath),
|
||||
Path.GetFullPath(targetImgDirPath),
|
||||
distanceDifference,
|
||||
Path.GetFullPath(rotScaleInfoFilePath));
|
||||
Debug.Log("cmd: " + cmd);
|
||||
_ = RunCmdAsync(cmd, (output, error) =>
|
||||
{
|
||||
Debug.Log(output);
|
||||
if (!File.Exists(rotScaleInfoFilePath))
|
||||
{
|
||||
Debug.LogError($"[E] 文件{rotScaleInfoFilePath} 未能正确获得");
|
||||
Debug.LogError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
using (StreamReader reader = File.OpenText(rotScaleInfoFilePath))
|
||||
{
|
||||
var jsonData = reader.ReadToEnd();
|
||||
RotScaleJsonData rotScaleJsonData = JsonConvert.DeserializeObject<RotScaleJsonData>(jsonData);
|
||||
callback(rotScaleJsonData);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 执行 cmd 命令
|
||||
public static void RunCmd(in string cmd)
|
||||
{
|
||||
var p = new System.Diagnostics.Process();
|
||||
p.StartInfo.FileName = "cmd.exe";
|
||||
p.StartInfo.Arguments = "/c " + cmd; // 使用 /c 参数执行命令并关闭 cmd 窗口
|
||||
p.StartInfo.UseShellExecute = false;
|
||||
p.StartInfo.CreateNoWindow = true;
|
||||
p.StartInfo.RedirectStandardOutput = true;
|
||||
p.StartInfo.RedirectStandardError = true;
|
||||
p.StartInfo.StandardOutputEncoding = System.Text.Encoding.UTF8;
|
||||
p.StartInfo.StandardErrorEncoding = System.Text.Encoding.UTF8;
|
||||
|
||||
p.Start();
|
||||
var output = p.StandardOutput.ReadToEnd();
|
||||
var error = p.StandardError.ReadToEnd();
|
||||
p.WaitForExit();
|
||||
|
||||
UnityEngine.Debug.Log("cmd output : " + output);
|
||||
UnityEngine.Debug.Log("cmd error : " + error);
|
||||
}
|
||||
|
||||
// 异步执行 cmd 命令
|
||||
public static async Task RunCmdAsync(string cmd, Action<string, string> callback = null)
|
||||
{
|
||||
var p = new System.Diagnostics.Process();
|
||||
p.StartInfo.FileName = "cmd.exe";
|
||||
p.StartInfo.Arguments = "/c " + cmd; // 使用 /c 参数执行命令并关闭 cmd 窗口
|
||||
p.StartInfo.UseShellExecute = false;
|
||||
p.StartInfo.CreateNoWindow = true;
|
||||
p.StartInfo.RedirectStandardOutput = true;
|
||||
p.StartInfo.RedirectStandardError = true;
|
||||
p.StartInfo.StandardOutputEncoding = System.Text.Encoding.UTF8;
|
||||
p.StartInfo.StandardErrorEncoding = System.Text.Encoding.UTF8;
|
||||
|
||||
p.Start();
|
||||
|
||||
var output = await p.StandardOutput.ReadToEndAsync();
|
||||
var error = await p.StandardError.ReadToEndAsync();
|
||||
// 异步等待进程退出
|
||||
await Task.Run(() => p.WaitForExit());
|
||||
|
||||
// 在主线程上调用回调函数
|
||||
UnityMainThreadDispatcher.Instance().Enqueue(() => callback?.Invoke(output, error));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c5a6dac91d434baab2b66ee97122eaf3
|
||||
timeCreated: 1718813946
|
@ -1,7 +1,10 @@
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using OpenCVForUnity.CoreModule;
|
||||
using OpenCVForUnity.Features2dModule;
|
||||
using OpenCVForUnity.ImgprocModule;
|
||||
using OpenCVForUnity.Calib3dModule;
|
||||
using OpenCVForUnity.UnityUtils;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
@ -9,9 +12,16 @@ using UnityEngine;
|
||||
using OpenCVForUnity.ImgcodecsModule;
|
||||
using System.Threading.Tasks;
|
||||
using UguiToolkit.Editor;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
public static class ImageUtils
|
||||
{
|
||||
public static void SetDebugMode(bool debug)
|
||||
{
|
||||
Utils.setDebugMode(debug);
|
||||
}
|
||||
|
||||
public static string FormatImgFilePath(string imgFilePath)
|
||||
{
|
||||
string projectPath = Directory.GetParent(Application.dataPath).FullName;
|
||||
@ -31,31 +41,60 @@ public static class ImageUtils
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task ProcessFolderAsync(List<Mat> images, string targetFilePath, RotationScaleDetector detector,
|
||||
double distanceDifference, Action<int, (double, (double, double))> callback,
|
||||
static List<Task<(double?, (double, double)?)>> tasks;
|
||||
static ObjectPool<RotationScaleDetector> detectorPool;
|
||||
|
||||
public static async Task ProcessFolderAsync(List<Mat> images, string targetFilePath,
|
||||
double distanceDifference, Action<int, (double, (double, double), bool)> callback,
|
||||
Action endCallback)
|
||||
{
|
||||
Debug.Log("GetRotationScaleAsync: " + targetFilePath);
|
||||
if (tasks == null) tasks = new (images.Count);
|
||||
if (detectorPool == null) detectorPool = new (images.Count);
|
||||
|
||||
Mat targetImage = Imgcodecs.imread(targetFilePath);
|
||||
for (int index = 0; index < images.Count; index++)
|
||||
|
||||
List<RotationScaleDetector> detectors = new(images.Count);
|
||||
tasks.Clear();
|
||||
foreach (var img in images)
|
||||
{
|
||||
RotationScaleDetector detector = detectorPool.GetObject();
|
||||
tasks.Add(detector.GetRotationScaleAsync(targetImage, img, distanceDifference));
|
||||
detectors.Add(detector);
|
||||
}
|
||||
|
||||
var resultsTask = await Task.WhenAll(tasks.ToArray());
|
||||
foreach (var detector in detectors) detectorPool.ReturnObject(detector);
|
||||
|
||||
for (int index = 0; index < resultsTask.Length; index++)
|
||||
{
|
||||
var img = images[index];
|
||||
var result = await detector.GetRotationScaleAsync(targetImage, img, distanceDifference);
|
||||
int _index = index;
|
||||
var result = resultsTask[index];
|
||||
UnityMainThreadDispatcher.Instance().Enqueue(() =>
|
||||
{
|
||||
if (result.Item1.HasValue && result.Item2.HasValue)
|
||||
if (result.Item1.HasValue)
|
||||
{
|
||||
double rotationAngleDegrees = result.Item1.Value;
|
||||
var scale = result.Item2.Value;
|
||||
double scaleX = scale.Item1;
|
||||
double scaleY = scale.Item2;
|
||||
double scaleX = 0;
|
||||
double scaleY = 0;
|
||||
|
||||
if (result.Item2.HasValue)
|
||||
{
|
||||
var scale = result.Item2.Value;
|
||||
scaleX = scale.Item1;
|
||||
scaleY = scale.Item2;
|
||||
|
||||
callback(_index, (rotationAngleDegrees, (scaleX, scaleY), false));
|
||||
}
|
||||
else
|
||||
{ // SimilarityCalc
|
||||
callback(_index, (rotationAngleDegrees, (scaleX, scaleY), true));
|
||||
}
|
||||
|
||||
callback(_index, (rotationAngleDegrees, (scaleX, scaleY)));
|
||||
Debug.Log($"Target Image -> Image {_index}");
|
||||
Debug.Log($"Rotation Angle: {rotationAngleDegrees} degrees");
|
||||
Debug.Log($"Scale X: {scaleX}");
|
||||
Debug.Log($"Scale Y: {scaleY}");
|
||||
Debug.Log($"SimilarityCalc : {result.Item2 == null}");
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -80,6 +119,9 @@ public class RotationScaleDetector
|
||||
private List<DMatch> goodMatches;
|
||||
private List<Point> srcPts;
|
||||
private List<Point> dstPts;
|
||||
private Mat resizedImage;
|
||||
private Mat dctImage;
|
||||
StringBuilder sb;
|
||||
|
||||
public RotationScaleDetector()
|
||||
{
|
||||
@ -94,6 +136,9 @@ public class RotationScaleDetector
|
||||
goodMatches = new List<DMatch>();
|
||||
srcPts = new List<Point>();
|
||||
dstPts = new List<Point>();
|
||||
resizedImage = new Mat();
|
||||
dctImage = new Mat();
|
||||
sb = new StringBuilder();
|
||||
}
|
||||
|
||||
private KeyPoint[] Sift(Mat image, Mat descriptors)
|
||||
@ -103,8 +148,132 @@ public class RotationScaleDetector
|
||||
return keypoints.toArray();
|
||||
}
|
||||
|
||||
#region MD5
|
||||
public string CalculateMD5(Mat image)
|
||||
{
|
||||
// 将Mat转换为字节数组
|
||||
byte[] imageBytes = new byte[image.total() * image.elemSize()];
|
||||
image.get(0, 0, imageBytes);
|
||||
|
||||
// 创建MD5哈希算法实例
|
||||
using (MD5 md5 = MD5.Create())
|
||||
{
|
||||
// 计算哈希值
|
||||
byte[] hashBytes = md5.ComputeHash(imageBytes);
|
||||
|
||||
// 将哈希字节数组转换为十六进制字符串
|
||||
sb.Clear();
|
||||
foreach (byte b in hashBytes)
|
||||
{
|
||||
sb.Append(b.ToString("x2"));
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Phash
|
||||
private Mat CalculatePhash(Mat image)
|
||||
{
|
||||
Imgproc.cvtColor(image, gray, Imgproc.COLOR_BGR2GRAY);
|
||||
// 调整大小为32x32,并转换为32位浮点类型
|
||||
Imgproc.resize(gray, resizedImage, new Size(32, 32), 0, 0, Imgproc.INTER_AREA);
|
||||
resizedImage.convertTo(resizedImage, CvType.CV_32F);
|
||||
// 进行离散余弦变换(DCT)
|
||||
Core.dct(resizedImage, dctImage);
|
||||
// 检查矩阵大小
|
||||
if (dctImage.rows() < 8 || dctImage.cols() < 8)
|
||||
{
|
||||
Debug.LogError("DCT matrix is too small!");
|
||||
return new Mat();
|
||||
}
|
||||
// 取左上角8x8的DCT系数
|
||||
Mat dctLowFreq = dctImage.submat(new OpenCVForUnity.CoreModule.Rect(0, 0, 8, 8));
|
||||
// 将DCT系数转换为数组
|
||||
float[] dctArray = new float[64];
|
||||
dctLowFreq.get(0, 0, dctArray);
|
||||
// 计算中值
|
||||
float medianValue = GetMedian(dctArray);
|
||||
// 生成pHash
|
||||
Mat phash = new Mat(dctLowFreq.size(), CvType.CV_8U);
|
||||
for (int i = 0; i < dctLowFreq.rows(); i++)
|
||||
{
|
||||
for (int j = 0; j < dctLowFreq.cols(); j++)
|
||||
{
|
||||
phash.put(i, j, dctLowFreq.get(i, j)[0] > medianValue ? 1 : 0);
|
||||
}
|
||||
}
|
||||
return phash;
|
||||
}
|
||||
|
||||
private float GetMedian(float[] array)
|
||||
{
|
||||
// 排序数组
|
||||
System.Array.Sort(array);
|
||||
// 计算中值
|
||||
int middle = array.Length / 2;
|
||||
if (array.Length % 2 == 0)
|
||||
{
|
||||
return (array[middle - 1] + array[middle]) / 2.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return array[middle];
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsPhashValid(Mat phash)
|
||||
{
|
||||
// 计算pHash中为1的位数
|
||||
return Core.countNonZero(phash) >= 13;
|
||||
}
|
||||
|
||||
private int CalculateHammingDistance(Mat phash1, Mat phash2)
|
||||
{
|
||||
if (phash1.rows() != phash2.rows() || phash1.cols() != phash2.cols())
|
||||
{
|
||||
Debug.LogError("pHash sizes do not match!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int hammingDistance = 0;
|
||||
for (int i = 0; i < phash1.rows(); i++)
|
||||
{
|
||||
for (int j = 0; j < phash1.cols(); j++)
|
||||
{
|
||||
if (phash1.get(i, j)[0] != phash2.get(i, j)[0])
|
||||
{
|
||||
hammingDistance++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hammingDistance;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public async Task<(double?, (double, double)?)> GetRotationScaleAsync(Mat img0, Mat img1, double distanceDifference)
|
||||
{
|
||||
return await Task.Run<(double?, (double, double)?)>(() => {
|
||||
try
|
||||
{
|
||||
string md5Hash0 = CalculateMD5(img0);
|
||||
string md5Hash1 = CalculateMD5(img1);
|
||||
if (md5Hash0 == md5Hash1)
|
||||
{
|
||||
return (0, null);
|
||||
}
|
||||
|
||||
Mat phash1 = CalculatePhash(img0);
|
||||
Mat phash2 = CalculatePhash(img1);
|
||||
|
||||
if (IsPhashValid(phash1) && IsPhashValid(phash2) && CalculateHammingDistance(phash1, phash2) < 10)
|
||||
{
|
||||
return (0, null);
|
||||
}
|
||||
|
||||
KeyPoint[] kp1 = Sift(img0, descriptors1);
|
||||
KeyPoint[] kp2 = Sift(img1, descriptors2);
|
||||
|
||||
@ -157,5 +326,14 @@ public class RotationScaleDetector
|
||||
|
||||
return (rotationAngleDegrees, (scaleX, scaleY));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
|
||||
return (null, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
49
Assets/Editor/Helper/ObjectPool.cs
Normal file
49
Assets/Editor/Helper/ObjectPool.cs
Normal file
@ -0,0 +1,49 @@
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UguiToolkit.Editor
|
||||
{
|
||||
public class ObjectPool<T> where T : new()
|
||||
{
|
||||
public int initialSize = 10; // 初始大小
|
||||
private Queue<T> pool;
|
||||
|
||||
public ObjectPool(int initialSize)
|
||||
{
|
||||
this.initialSize = initialSize;
|
||||
this.pool = new Queue<T>(initialSize);
|
||||
// 预先创建一些对象
|
||||
for (int i = 0; i < initialSize; i++)
|
||||
{
|
||||
T obj = new T();
|
||||
pool.Enqueue(obj);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取对象
|
||||
public T GetObject()
|
||||
{
|
||||
if (pool.Count > 0)
|
||||
{
|
||||
T obj = pool.Dequeue();
|
||||
return obj;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果池中没有可用对象,则创建新的对象
|
||||
T obj = new T();
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
// 释放对象
|
||||
public void ReturnObject(T obj)
|
||||
{
|
||||
pool.Enqueue(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
11
Assets/Editor/Helper/ObjectPool.cs.meta
Normal file
11
Assets/Editor/Helper/ObjectPool.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc8e87f03da56414b9de0b42bc4fd282
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -35,6 +35,23 @@ namespace UguiToolkit.Editor
|
||||
PanelCacheWindow.CloseWindow();
|
||||
EditWindow.CloseWindow();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建热键 SPACE
|
||||
/// </summary>
|
||||
/// <param name="menuCommand"></param>
|
||||
[MenuItem("GameObject/拼接助手/应用上次变换 _SPACE", false, 5)]
|
||||
public static void JumpOfGameCameraByCurPos(MenuCommand menuCommand)
|
||||
{
|
||||
if (StageManager.Instance)
|
||||
{
|
||||
var entityMng = StageManager.Instance.GetManager<EntityManager>();
|
||||
if (entityMng && Selection.activeGameObject)
|
||||
{
|
||||
entityMng.EffectLastApplyTransform(Selection.activeGameObject.transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -36,14 +36,10 @@ namespace UguiToolkit.Editor
|
||||
|
||||
private bool m_useTMP;
|
||||
private EditWindow editWindow;
|
||||
private string m_runningImgDirPath = null;
|
||||
private bool isRunning = false;
|
||||
private HashSet<string> m_runningImgFilePaths = new();
|
||||
private List<string> m_preCheckImgDirPaths = new ();
|
||||
private bool isCalcRotationScaleRunning = false;
|
||||
|
||||
private List<OpenCVForUnity.CoreModule.Mat> targetImages;
|
||||
private List<string> targetPaths;
|
||||
private RotationScaleDetector detector;
|
||||
|
||||
public Transform Background => m_background;
|
||||
|
||||
@ -62,7 +58,13 @@ namespace UguiToolkit.Editor
|
||||
editWindow.showHierarchyOfEntityChanged += OnUpdateHierarchyOfEntityAllEntity;
|
||||
editWindow.showBackgroundChanged += OnUpdateBackgroundShow;
|
||||
editWindow.createAllTextEntity += CreateAllTextEntity;
|
||||
editWindow.createAllPrefabEntity += CreateAllPrefabEntity;
|
||||
editWindow.effectLastApplyTransform += EffectLastApplyTransform;
|
||||
}
|
||||
|
||||
EditorApplication.hierarchyWindowItemOnGUI += HandleHierarchyWindowItemOnGUI;
|
||||
|
||||
ImageUtils.SetDebugMode(true);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
@ -74,7 +76,15 @@ namespace UguiToolkit.Editor
|
||||
editWindow.showHierarchyOfEntityChanged -= OnUpdateHierarchyOfEntityAllEntity;
|
||||
editWindow.showBackgroundChanged -= OnUpdateBackgroundShow;
|
||||
editWindow.createAllTextEntity -= CreateAllTextEntity;
|
||||
editWindow.createAllPrefabEntity -= CreateAllPrefabEntity;
|
||||
editWindow.effectLastApplyTransform -= EffectLastApplyTransform;
|
||||
}
|
||||
|
||||
EditorApplication.hierarchyWindowItemOnGUI -= HandleHierarchyWindowItemOnGUI;
|
||||
|
||||
ImageUtils.SetDebugMode(false);
|
||||
if (m_stageManager.PrefabAsset || m_panelCache != null)
|
||||
GlobalManager.Instance.SaveCache(m_stageManager.PrefabAsset, m_panelCache);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
@ -115,7 +125,6 @@ namespace UguiToolkit.Editor
|
||||
entity.ApplyData(temp);
|
||||
}
|
||||
|
||||
if (editWindow) editWindow.SetLastApplyEntity(entity);
|
||||
m_lastSelectionGo = m_curSelectionGo;
|
||||
m_lastSelectionEntity = entity;
|
||||
|
||||
@ -124,58 +133,33 @@ namespace UguiToolkit.Editor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 及时更新PanelCache
|
||||
CheckPanelCache();
|
||||
}
|
||||
|
||||
public void AddCheckImgDirPath(string dirPath)
|
||||
public void EffectLastApplyTransform(Transform parent)
|
||||
{
|
||||
//if (string.IsNullOrEmpty(dirPath)) return;
|
||||
//dirPath = dirPath.Replace("\\", "/");
|
||||
|
||||
//if (m_runningImgDirPath != null && m_runningImgDirPath == dirPath) return;
|
||||
|
||||
//if (m_panelCache.IsValidOfImgDirPath(dirPath))
|
||||
//{
|
||||
// return;
|
||||
//}
|
||||
|
||||
//if (Directory.Exists(dirPath))
|
||||
//{
|
||||
// if (m_preCheckImgDirPaths.Contains(dirPath))
|
||||
// {
|
||||
// m_preCheckImgDirPaths.Remove(dirPath);
|
||||
// }
|
||||
|
||||
// Debug.Log($"[I] AddCheckImgDirPath {dirPath}");
|
||||
// m_preCheckImgDirPaths.Insert(0, dirPath);
|
||||
//}
|
||||
}
|
||||
|
||||
private string GetCheckImgDirPath()
|
||||
if (m_lastSelectionEntity != null)
|
||||
{
|
||||
if (m_preCheckImgDirPaths.Count > 0)
|
||||
{
|
||||
string dirPath = m_preCheckImgDirPaths[0];
|
||||
m_preCheckImgDirPaths.RemoveAt(0);
|
||||
if (Directory.Exists(dirPath))
|
||||
{
|
||||
return dirPath;
|
||||
m_lastSelectionEntity.ApplyTransformByParent(parent, m_lastSelectionGo ? m_lastSelectionGo.transform: null);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void CheckPanelCache()
|
||||
private void HandleHierarchyWindowItemOnGUI(int instanceID, Rect selectionRect)
|
||||
{
|
||||
if (m_runningImgDirPath == null)
|
||||
if (!m_stageManager) return;
|
||||
var go = EditorUtility.InstanceIDToObject(instanceID) as GameObject;
|
||||
if (!go) return;
|
||||
if (go == m_lastSelectionGo)
|
||||
{
|
||||
var srcImgDirPath = GetCheckImgDirPath();
|
||||
if (srcImgDirPath != null)
|
||||
var goLabel = new GUIContent(go.name);
|
||||
var goSize = EditorStyles.linkLabel.CalcSize(goLabel) + new Vector2(20, 0);
|
||||
var label = new GUIContent(" <-----");
|
||||
var size = EditorStyles.linkLabel.CalcSize(label) + new Vector2(10, 0);
|
||||
var offsetRect =
|
||||
new Rect(selectionRect.position + new Vector2(goSize.x, 0), size);
|
||||
EditorGUI.LabelField(offsetRect, label, new GUIStyle()
|
||||
{
|
||||
UpdatePanelCache(srcImgDirPath, m_panelCache.TargetImgDirPath);
|
||||
}
|
||||
normal = new GUIStyleState() { textColor = Color.green },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,7 +168,6 @@ namespace UguiToolkit.Editor
|
||||
var root = m_stageManager.PrefabContentsRoot;
|
||||
var textsTf = root.transform.Find("__texts__");
|
||||
if (textsTf) DestroyImmediate(textsTf.gameObject);
|
||||
|
||||
if (m_textEntities == null) return;
|
||||
var textsGo = new GameObject("__texts__", typeof(RectTransform));
|
||||
textsGo.transform.parent = root.transform;
|
||||
@ -199,6 +182,28 @@ namespace UguiToolkit.Editor
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateAllPrefabEntity()
|
||||
{
|
||||
var root = m_stageManager.PrefabContentsRoot;
|
||||
var prefabsTf = root.transform.Find("__prefabs__");
|
||||
if (prefabsTf) DestroyImmediate(prefabsTf.gameObject);
|
||||
|
||||
if (m_textEntities == null) return;
|
||||
var prefabsGo = new GameObject("__prefabs__", typeof(RectTransform));
|
||||
prefabsGo.transform.parent = root.transform;
|
||||
|
||||
foreach (var prefabEntity in m_prefabEntities)
|
||||
{
|
||||
var asset = prefabEntity.GetPrefabAsset(PrefabEntity.GetCommonDirPath());
|
||||
if (asset != null)
|
||||
{
|
||||
var go = GameObject.Instantiate(asset, prefabsGo.transform);
|
||||
go.name = asset.name;
|
||||
prefabEntity.ApplyData(go.transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InitBackground()
|
||||
{
|
||||
if (m_background) DestroyImmediate(m_background.gameObject);
|
||||
@ -242,10 +247,10 @@ namespace UguiToolkit.Editor
|
||||
private void UpdatePanelCache(string srcImgPath)
|
||||
{
|
||||
float distanceDifference = GlobalManager.Instance.setting.distanceDifference;
|
||||
m_runningImgFilePaths.Add(srcImgPath);
|
||||
isRunning = true;
|
||||
isCalcRotationScaleRunning = true;
|
||||
List<RotScaleInfoItem> rotScaleItemList = new();
|
||||
_ = ImageUtils.ProcessFolderAsync(targetImages, srcImgPath, detector, distanceDifference, (index, result) =>
|
||||
Debug.Log("GetRotationScaleAsync: " + srcImgPath);
|
||||
_ = ImageUtils.ProcessFolderAsync(targetImages, srcImgPath, distanceDifference, (index, result) =>
|
||||
{
|
||||
if (!m_stageManager) return;
|
||||
rotScaleItemList.Add(new()
|
||||
@ -253,13 +258,13 @@ namespace UguiToolkit.Editor
|
||||
imgPath = ImageUtils.FormatImgFilePath(targetPaths[index]),
|
||||
rotiation = (float)result.Item1,
|
||||
scale = new float2((float)result.Item2.Item1, (float)result.Item2.Item1),
|
||||
similarityCalc = false
|
||||
similarityCalc = result.Item3
|
||||
});
|
||||
}, () =>
|
||||
{
|
||||
isRunning = false;
|
||||
Debug.Log("End GetRotationScaleAsync: " + srcImgPath);
|
||||
isCalcRotationScaleRunning = false;
|
||||
if (!m_stageManager) return;
|
||||
m_runningImgFilePaths.Remove(srcImgPath);
|
||||
|
||||
m_panelCache.AddRotScaleInfo(srcImgPath, rotScaleItemList);
|
||||
if (rotScaleItemList.Count > 0)
|
||||
@ -267,26 +272,6 @@ namespace UguiToolkit.Editor
|
||||
});
|
||||
}
|
||||
|
||||
public void UpdatePanelCache(string srcImgDirPath, string targetImgDirPath)
|
||||
{
|
||||
float distanceDifference = GlobalManager.Instance.setting.distanceDifference;
|
||||
m_runningImgDirPath = srcImgDirPath;
|
||||
|
||||
CacheScriptObject.CalcRotScaleInfos(srcImgDirPath, targetImgDirPath, distanceDifference,(rotScaleInfoMap) =>
|
||||
{
|
||||
m_runningImgDirPath = null;
|
||||
if (!m_stageManager) return;
|
||||
m_panelCache.AddImgDirPathTimestamp(srcImgDirPath);
|
||||
m_panelCache.ClearRotScaleInfoItem(srcImgDirPath);
|
||||
|
||||
// 拷贝数据
|
||||
foreach (var kv in rotScaleInfoMap) m_panelCache.AddRotScaleInfo(kv.Key, kv.Value);
|
||||
|
||||
// 保存缓存
|
||||
GlobalManager.Instance.SaveCache(m_stageManager.PrefabAsset, m_panelCache);
|
||||
});
|
||||
}
|
||||
|
||||
private void OnSelectionChanged()
|
||||
{
|
||||
if (m_noSelection || !Selection.activeGameObject || Selection.gameObjects.Length > 1) return;
|
||||
@ -310,6 +295,7 @@ namespace UguiToolkit.Editor
|
||||
{
|
||||
prefabEntity.ShowSelectionImg(true);
|
||||
prefabEntity.gameObject.SetActive(true);
|
||||
prefabEntity.SetOriginalMatrix(activeGameObject.transform);
|
||||
|
||||
m_selectionEntities.Add(prefabEntity);
|
||||
|
||||
@ -345,6 +331,7 @@ namespace UguiToolkit.Editor
|
||||
if (!string.IsNullOrEmpty(srcImgPath) && m_panelCache.HaveRotScaleInfo(srcImgPath, out var rotScaleInfoItems))
|
||||
{
|
||||
m_entityRoot.gameObject.SetActive(true);
|
||||
|
||||
bool isFind;
|
||||
bool IsInside = false;
|
||||
foreach (var imgEntity in m_imageEntities)
|
||||
@ -364,8 +351,9 @@ namespace UguiToolkit.Editor
|
||||
imgEntity.SetTransform(rotScale.rotiation, rotScale.scale, false);
|
||||
}
|
||||
|
||||
imgEntity.ApplyTransform(imgEntity.transform);
|
||||
imgEntity.InternalApplyTransform(imgEntity.transform);
|
||||
imgEntity.ShowSelectionImg(true);
|
||||
imgEntity.SetOriginalMatrix(activeGameObject.transform);
|
||||
m_selectionEntities.Add(imgEntity);
|
||||
|
||||
if (!IsInside && imgEntity.IsInside(activeGameObject.transform)) IsInside = true;
|
||||
@ -409,6 +397,7 @@ namespace UguiToolkit.Editor
|
||||
{
|
||||
textEntity.ShowSelectionImg(true);
|
||||
textEntity.gameObject.SetActive(true);
|
||||
textEntity.SetOriginalMatrix(activeGameObject.transform);
|
||||
|
||||
m_selectionEntities.Add(textEntity);
|
||||
|
||||
@ -444,7 +433,7 @@ namespace UguiToolkit.Editor
|
||||
{
|
||||
var srcImgPath = AssetDatabase.GetAssetPath(image.sprite);
|
||||
if (!string.IsNullOrEmpty(srcImgPath) && !m_panelCache.HaveRotScaleInfo(srcImgPath) &&
|
||||
!isRunning)
|
||||
!isCalcRotationScaleRunning)
|
||||
{
|
||||
UpdatePanelCache(srcImgPath);
|
||||
|
||||
@ -467,7 +456,6 @@ namespace UguiToolkit.Editor
|
||||
targetImages = new ();
|
||||
targetPaths = new ();
|
||||
ImageUtils.LoadPngImagesFromFolder(panelCache.TargetImgDirPath, targetImages, targetPaths);
|
||||
detector = new RotationScaleDetector();
|
||||
}
|
||||
|
||||
private void OnUpdateBackgroundShow(bool show)
|
||||
@ -482,8 +470,10 @@ namespace UguiToolkit.Editor
|
||||
{
|
||||
UpdateHierarchyOfEntity(show, m_entityRoot.gameObject);
|
||||
UpdateHierarchyOfEntity(show, m_background.gameObject);
|
||||
foreach (Transform entity in m_background.transform) UpdateHierarchyOfEntity(show, entity.gameObject);
|
||||
foreach (var entity in m_imageEntities) UpdateHierarchyOfEntity(show, entity.gameObject);
|
||||
foreach (var entity in m_textEntities) UpdateHierarchyOfEntity(show, entity.gameObject);
|
||||
foreach (var entity in m_prefabEntities) UpdateHierarchyOfEntity(show, entity.gameObject);
|
||||
}
|
||||
|
||||
private void UpdateHierarchyOfEntity(in bool show, in GameObject entity)
|
||||
|
@ -53,35 +53,6 @@ namespace UguiToolkit.Editor
|
||||
return layoutInfo;
|
||||
}
|
||||
}
|
||||
|
||||
public static void CalcRotScaleInfos(string srcImgDirPath, string targetImgDirPath, float distanceDifference, Action<Dictionary<string, List<RotScaleInfoItem>>> callback)
|
||||
{
|
||||
// 执行cmd
|
||||
CommandHelper.CalcRotScale(srcImgDirPath, targetImgDirPath, distanceDifference,(jsonData) =>
|
||||
{
|
||||
if (jsonData == null || jsonData.data == null) return;
|
||||
Dictionary<string, List<RotScaleInfoItem>> rotScaleInfos = new();
|
||||
|
||||
string projectPath = Directory.GetParent(Application.dataPath).FullName;
|
||||
foreach (var kv in jsonData.data)
|
||||
{
|
||||
List<RotScaleInfoItem> rotScaleItemList = new();
|
||||
rotScaleInfos[ImageUtils.FormatImgFilePath(kv.Key)] = rotScaleItemList;
|
||||
foreach (var jsonItemData in kv.Value)
|
||||
{
|
||||
rotScaleItemList.Add(new()
|
||||
{
|
||||
imgPath = ImageUtils.FormatImgFilePath(jsonItemData.targetPath),
|
||||
rotiation = jsonItemData.rot,
|
||||
scale = new float2(jsonItemData.scale[0], jsonItemData.scale[1]),
|
||||
similarityCalc = jsonItemData.similarityCalc
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
callback(rotScaleInfos);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
@ -94,8 +65,6 @@ namespace UguiToolkit.Editor
|
||||
[Serializable]
|
||||
public class PanelCache
|
||||
{
|
||||
[LabelText("项目内导出图片文件夹"), FolderPath]
|
||||
public string srcImgDirPath;
|
||||
[LabelText("目标图片信息文件(psd导出)"), Sirenix.OdinInspector.FilePath(AbsolutePath = true, Extensions = "layout.txt")]
|
||||
public string layoutInfoFilePath; // Sample.layout.txt
|
||||
[LabelText("目标图片文件夹路径")]
|
||||
@ -123,15 +92,12 @@ namespace UguiToolkit.Editor
|
||||
public LayoutInfo layoutInfo;
|
||||
[SerializeField, HideInInspector]
|
||||
private string m_targetImgDirPath;
|
||||
[SerializeField]
|
||||
public Dictionary<string, long> imgDirPathTimestamp = new();
|
||||
|
||||
private PanelCache() { }
|
||||
|
||||
// public PanelCache(string srcImgDirPath, string layoutInfoFilePath)
|
||||
public PanelCache(string layoutInfoFilePath, string srcImgDirPath, bool isVertical = false)
|
||||
public PanelCache(string layoutInfoFilePath, bool isVertical = false)
|
||||
{
|
||||
this.srcImgDirPath = srcImgDirPath;
|
||||
this.layoutInfoFilePath = layoutInfoFilePath;
|
||||
this.isVertical = isVertical;
|
||||
|
||||
@ -141,7 +107,6 @@ namespace UguiToolkit.Editor
|
||||
public void Copy(PanelCache panelCache)
|
||||
{
|
||||
rotScaleInfos = panelCache.rotScaleInfos;
|
||||
imgDirPathTimestamp = panelCache.imgDirPathTimestamp;
|
||||
}
|
||||
|
||||
public bool HaveRotScaleInfo(string srcImgPath)
|
||||
@ -159,21 +124,21 @@ namespace UguiToolkit.Editor
|
||||
rotScaleInfos[srcImgPath] = rotScaleInfo;
|
||||
}
|
||||
|
||||
public bool IsValidOfImgDirPath(string imgDirPath)
|
||||
public void InitRotScaleInfos()
|
||||
{
|
||||
if (!Directory.Exists(imgDirPath)) return false;
|
||||
|
||||
if (imgDirPathTimestamp.TryGetValue(imgDirPath, out var timestamp))
|
||||
HashSet<string> keys = new();
|
||||
foreach (var srcImgPath in rotScaleInfos.Keys)
|
||||
{
|
||||
var curTimestamp = GetTimestampByDirPath(imgDirPath);
|
||||
return curTimestamp == timestamp;
|
||||
if (!File.Exists(srcImgPath))
|
||||
{
|
||||
keys.Add(srcImgPath);
|
||||
}
|
||||
}
|
||||
foreach (var key in keys)
|
||||
{
|
||||
rotScaleInfos.Remove(key);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ClearRotScaleInfoItem(string imgDirPath)
|
||||
{
|
||||
List<int> indeces = new();
|
||||
foreach (var rotScaleInfo in rotScaleInfos.Values)
|
||||
{
|
||||
@ -181,7 +146,7 @@ namespace UguiToolkit.Editor
|
||||
for (var i = 0; i < rotScaleInfo.Count; i++)
|
||||
{
|
||||
var item = rotScaleInfo[i];
|
||||
if (item.imgPath.StartsWith(imgDirPath))
|
||||
if (!File.Exists(item.imgPath))
|
||||
{
|
||||
indeces.Add(i);
|
||||
}
|
||||
@ -197,27 +162,9 @@ namespace UguiToolkit.Editor
|
||||
}
|
||||
}
|
||||
|
||||
public void AddImgDirPathTimestamp(string imgDirPath)
|
||||
{
|
||||
var curTimestamp = GetTimestampByDirPath(imgDirPath);
|
||||
imgDirPathTimestamp[imgDirPath] = curTimestamp;
|
||||
}
|
||||
|
||||
public void InitRotScaleInfos()
|
||||
{
|
||||
foreach(var dirPath in imgDirPathTimestamp.Keys)
|
||||
{
|
||||
if (!IsValidOfImgDirPath(dirPath))
|
||||
{
|
||||
ClearRotScaleInfoItem(dirPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearRotScaleInfos()
|
||||
{
|
||||
rotScaleInfos.Clear();
|
||||
imgDirPathTimestamp.Clear();
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetLayoutElementInfos<T>() where T : LayoutInfo.ElementInfo
|
||||
@ -243,14 +190,6 @@ namespace UguiToolkit.Editor
|
||||
|
||||
return System.IO.Path.Join(dirName, split[0]);
|
||||
}
|
||||
|
||||
public static long GetTimestampByDirPath(string dirPath)
|
||||
{
|
||||
DirectoryInfo directoryInfo = new DirectoryInfo(dirPath);
|
||||
// 获取文件夹的最后修改时间
|
||||
DateTime lastModified = directoryInfo.LastWriteTime;
|
||||
return new DateTimeOffset(lastModified).ToUnixTimeSeconds();
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
|
@ -24,6 +24,16 @@ namespace UguiToolkit.Editor.Windows
|
||||
/// </summary>
|
||||
public event Action createAllTextEntity;
|
||||
|
||||
/// <summary>
|
||||
/// 创建所有PrefabEntity
|
||||
/// </summary>
|
||||
public event Action createAllPrefabEntity;
|
||||
|
||||
/// <summary>
|
||||
/// 应用上次变换
|
||||
/// </summary>
|
||||
public event Action<Transform> effectLastApplyTransform;
|
||||
|
||||
|
||||
[SerializeField, HideInInspector]
|
||||
private bool m_showHierarchyOfEntityChange = false;
|
||||
@ -59,31 +69,26 @@ namespace UguiToolkit.Editor.Windows
|
||||
createAllTextEntity?.Invoke();
|
||||
}
|
||||
|
||||
[Button("创建所有通用预制体")]
|
||||
private void CreateAllPrefabEntity()
|
||||
{
|
||||
createAllPrefabEntity?.Invoke();
|
||||
}
|
||||
|
||||
|
||||
[Title("工具")]
|
||||
[LabelText("上次变换的对象"), SerializeField]
|
||||
private IEntity m_lastApplyEntity;
|
||||
[LabelText("需要应用变换的对象"), SerializeField]
|
||||
private Transform m_targetTransform;
|
||||
|
||||
[Button("应用上次变换")]
|
||||
[Button("应用上次变换 (SPACE)")]
|
||||
private void EffectLastApplyTransform()
|
||||
{
|
||||
if (m_targetTransform)
|
||||
{
|
||||
var m_lastApplyTransform = m_lastApplyEntity.gameObject.transform;
|
||||
var parent = m_lastApplyTransform.parent;
|
||||
m_lastApplyTransform.parent = null;
|
||||
m_lastApplyEntity.ApplyTransformByParent(m_targetTransform);
|
||||
m_lastApplyTransform.parent = parent;
|
||||
effectLastApplyTransform?.Invoke(m_targetTransform);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetLastApplyEntity(IEntity lastApplyTransform)
|
||||
{
|
||||
this.m_lastApplyEntity = lastApplyTransform;
|
||||
}
|
||||
|
||||
public override string GettitleContent()
|
||||
{
|
||||
return "助手编辑界面";
|
||||
|
@ -17,8 +17,6 @@ namespace UguiToolkit.Editor.Windows
|
||||
private bool m_isVertical = false;
|
||||
|
||||
[Title("psd导出数据设置")]
|
||||
[LabelText("项目中切图文件夹"), FolderPath, SerializeField]
|
||||
private string m_srcImgDirPath;
|
||||
[LabelText("layout.txt文件"), Sirenix.OdinInspector.FilePath(AbsolutePath = true, Extensions = "layout.txt"), SerializeField]
|
||||
private string m_layoutInfoFilePath;
|
||||
|
||||
@ -30,6 +28,12 @@ namespace UguiToolkit.Editor.Windows
|
||||
[Button("开启助手", ButtonSizes.Medium)]
|
||||
private void DoStart()
|
||||
{
|
||||
if (string.IsNullOrEmpty(m_layoutInfoFilePath))
|
||||
{
|
||||
EditorUtility.DisplayDialog("提示", "请先填写配置", "好的");
|
||||
return;
|
||||
}
|
||||
|
||||
var stageManager = UguiToolkit.StageManager.CreateStageManager(m_prefabStage.scene, m_prefabStage.prefabContentsRoot, m_prefab);
|
||||
stageManager.gameObject.AddComponent<UnityMainThreadDispatcher>();
|
||||
|
||||
@ -37,7 +41,7 @@ namespace UguiToolkit.Editor.Windows
|
||||
EditWindow.ShowWindow(null);
|
||||
|
||||
var targetImgDirPath = PanelCache.GetTargetImgDirPath(m_layoutInfoFilePath);
|
||||
var panelCache = new PanelCache(m_layoutInfoFilePath, m_srcImgDirPath, m_isVertical);
|
||||
var panelCache = new PanelCache(m_layoutInfoFilePath, m_isVertical);
|
||||
panelCache.layoutInfo = CacheScriptObject.PaserLayout(m_layoutInfoFilePath, targetImgDirPath);
|
||||
|
||||
if (m_panelCache != null)
|
||||
@ -55,8 +59,6 @@ namespace UguiToolkit.Editor.Windows
|
||||
// stageManager.CreateSubManager<SelectionManager>();
|
||||
|
||||
entityManager.Init(panelCache);
|
||||
if (!string.IsNullOrEmpty(m_srcImgDirPath))
|
||||
entityManager.AddCheckImgDirPath(m_srcImgDirPath);
|
||||
CloseWindow();
|
||||
}
|
||||
|
||||
@ -82,12 +84,10 @@ namespace UguiToolkit.Editor.Windows
|
||||
var panelCache = GlobalManager.Instance.GetCache(m_prefab, m_isVertical);
|
||||
if (panelCache != null)
|
||||
{
|
||||
m_srcImgDirPath = panelCache.srcImgDirPath;
|
||||
m_layoutInfoFilePath = panelCache.layoutInfoFilePath;
|
||||
m_panelCache = panelCache;
|
||||
}
|
||||
else {
|
||||
m_srcImgDirPath = null;
|
||||
m_layoutInfoFilePath = null;
|
||||
m_panelCache = null;
|
||||
}
|
||||
|
11
README.md
11
README.md
@ -5,7 +5,7 @@
|
||||
> 1. psd 导出自动切图,并处理九宫格
|
||||
> 2. unity拼接时,对psd上所标记的像素图层进行吸附,设置位置和旋转、缩放
|
||||
> 3. unity拼接时,对psd上所标记的文本图层进行吸附,设置位置和旋转、缩放,并设置字体、字体大小、字体颜色、描边等
|
||||
> 4. (未实现) unity拼接时,对公共组件预制体进行设置位置和旋转、缩放
|
||||
> 4. unity拼接时,对公共组件预制体进行设置位置和旋转、缩放
|
||||
|
||||
## 工作流
|
||||

|
||||
@ -32,13 +32,10 @@
|
||||
|
||||
### unity中如何操作
|
||||
|
||||
上述`yueka_output`为最终进入项目的切图,可自行根据分类放入 `client\Assets\res\ui\atlas`
|
||||
|
||||
#### 进入预制体场景
|
||||
点击开启助手,设置如下路径后,点击`开启助手`
|
||||
|
||||
1. 项目内导出图片文件夹:填入项目内该功能的切图目录
|
||||
2. 目标图片信息文件夹: 填入导出psd数据的 ` yueka.layout.txt`
|
||||
1. 目标图片信息文件夹: 填入导出psd数据的 ` yueka.layout.txt`
|
||||
|
||||

|
||||
|
||||
@ -49,6 +46,6 @@
|
||||
##### 文本
|
||||

|
||||

|
||||
##### 预制体 (未实现)
|
||||
1. 取消节点选中后,会显示界面所有可供创建的预制体预览
|
||||
##### 预制体
|
||||
1. 选中预制体节点后,会显示界面所有可供创建的预制体预览
|
||||
2. 将鼠标光标放入预制体预览框,会自动创建预制体
|
Loading…
Reference in New Issue
Block a user