Skip to content

Instantly share code, notes, and snippets.

@lowteq
Created January 17, 2020 07:01
Show Gist options
  • Save lowteq/c1864c4c8f5e1d158e153b73a739cff6 to your computer and use it in GitHub Desktop.
Save lowteq/c1864c4c8f5e1d158e153b73a739cff6 to your computer and use it in GitHub Desktop.
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Linq;
#if UNITY_EDITOR
using UnityEditor;
public class BroochEditor : EditorWindow
{
private class PathTool
{
int selectindex = -1;
DrawTexture2D drawTexture2d = new DrawTexture2D();
Texture2D texture;
bool closed = false;
List<Vector2> veclist;
float r;
int canvasSize;
Color pathColor;
bool drawtriangles = false;
bool drawsurflines = false;
int[] trianglesindex;
public PathTool(int canvassize,float radius,Color pathCol){
r = radius;
canvasSize = canvassize;
pathColor = pathCol;
veclist = new List<Vector2>();
texture = new Texture2D(canvasSize,canvasSize,TextureFormat.RGBA32,false);
drawTexture2d = new DrawTexture2D();
trianglesindex = new int[]{};
}
public void createtriangles01(){
}
public void ShowListContentsInTheDebugLog<T>(List<T> list)
{
string log = "";
foreach (var content in list.Select((val, idx) => new { val, idx }))
{
if (content.idx == list.Count - 1)
log += content.val.ToString();
else
log += content.val.ToString() + ", ";
}
Debug.Log(log);
}
private static int mod(int a,int m){
var v = a - m * (int)Math.Floor((double)a/(double)m);
//Debug.LogFormat("{0} mod {1} = {2}",a,m,v);
return v;
}
public int[] triangles(){
List<Vector2> vecs = new List<Vector2>(veclist);
int c = vecs.Count;
//原点から最遠点のindexを求める
List<int> searchvecindex = Enumerable.Range(0, c).ToList();
List<int> trianglesindex = new List<int> { };
var orig = new Vector2(0f, 0f);
int index = 0;
float d = 0;
for (var i = 0; i < searchvecindex.Count; i++)
{
var dnew = dist2(vecs[searchvecindex[i]],orig);
if (dnew > d)
{
index = i;
d=dnew;
}
}
var a = vecs[(index + 1) % c] - vecs[index];
var b = vecs[mod(index - 1, c)] - vecs[index];
var origincross = cross(a, b);
var emargency = 0;
var flip = (int)(origincross/Math.Abs(origincross));
Debug.Log("■OriginCross " + origincross);
while(true){
Debug.Log("-------------");
Debug.Log("index:"+index);
ShowListContentsInTheDebugLog<int>(searchvecindex);
emargency++;
if(emargency>1000){
Debug.Log("EMARGENCY");
Debug.Log(searchvecindex.Count);
break;
}
var svic = searchvecindex.Count;
var aa = vecs[searchvecindex[mod(index + 1,svic)]] - vecs[searchvecindex[index % svic]];
var bb = vecs[searchvecindex[mod(index - 1,svic)]] - vecs[searchvecindex[index % svic]];
var abcross = cross(aa,bb);
// a,bの直積の正負と最初の直積の正負が一致しないなら(角度が180°以上なら)選択頂点をとなりに移動してやりなおす
var directf = (origincross > 0) == (abcross > 0);
if (!directf){
index = (index + 1) % svic;
continue;
}
//他の頂点がaとbからなる三角形に入ってないか
bool trianglesIn = false;
var v1 = veclist[searchvecindex[index%svic]];
var v2 = veclist[searchvecindex[mod(index-1,svic)]];
var v3 = veclist[searchvecindex[(index+1)%svic]];
for (var i = 0; i < Veclist.Count; i++)
{
if (i != searchvecindex[mod(index,svic)] && i != searchvecindex[mod(index + 1,svic)] && i != searchvecindex[mod(index - 1,svic)])
{
if (triangleIn(v1, v2, v3, veclist[i]))
{
trianglesIn = true;
break;
}
}
}
//他のすべての頂点がすべてa,bからなる三角形に入ってないならa,bを分割線として作り選択頂点を検索点列から除外
if (!trianglesIn)
{
trianglesindex.AddRange(new int[]{searchvecindex[mod(index ,svic)],searchvecindex[mod(index + 1, svic)],searchvecindex[mod(index - 1, svic)]});
searchvecindex.RemoveAt(index%svic);
}else{
index = mod((index + 1),svic);
}
ShowListContentsInTheDebugLog<int>(searchvecindex);
if (searchvecindex.Count == 3)
{
trianglesindex.AddRange(new int[] { searchvecindex[mod(index, 3)], searchvecindex[mod(index + 1, 3)], searchvecindex[mod(index - 1, 3)] });
break;
}
}
this.trianglesindex = trianglesindex.ToArray();
return this.trianglesindex;
}
private static float sign(Vector2 p1,Vector2 p2,Vector2 p3){
return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y);
}
private static bool PointInTriangle(Vector2 v1,Vector2 v2,Vector2 v3,Vector2 p){
bool b1,b2,b3;
b1 = sign(p, v1, v2) < 0.0f;
b2 = sign(p, v2, v3) < 0.0f;
b3 = sign(p, v3, v1) < 0.0f;
Debug.Log(b1.ToString() + b2.ToString() + b3.ToString());
return (b1== b2 == b3);
}
private static bool triangleIn(Vector2 t1,Vector2 t2,Vector2 t3,Vector2 p){
var a = cross(t1-t2,p-t2);
var b = cross(t2-t3,p-t3);
var c = cross(t3-t1,p-t1);
Debug.Log("abc:" + a.ToString() + " " + b.ToString() + " " + c.ToString());
if(((a > 0) == (b > 0)) && ((b > 0) == (c > 0))){
return true;
}else{
return false;
}
}
private static float cross(Vector2 v1,Vector2 v2){
return v1.x*v2.y - v2.x*v1.y;
}
/// <summary>
/// 点リスト コピーされて渡される
/// </summary>
/// <value></value>
public List<Vector2> Veclist{
get{return new List<Vector2>(this.veclist);}
}
public bool Closed{
get{return this.closed;}
}
public Color PathColor{
set{this.pathColor = value;}
get{return this.pathColor;}
}
public bool DrawTriangles{
set{this.drawtriangles = value;}
get{return this.drawtriangles;}
}
public bool Drawsurflines{
set{this.drawsurflines = value;}
get{return drawsurflines;}
}
public Texture2D canvas(){
drawTexture2d.Begin(texture);
drawTexture2d.Clear(Color.clear);
var trianglec = Color.red;
var c = pathColor == null ? new Color(0.5f, 0.5f, 0.5f) : pathColor;
if (veclist.Count == 0){
drawTexture2d.End();
return texture;
}else{
if (drawtriangles)
{
drawTexture2d.Clear(Color.clear);
for (var i = 0; i < trianglesindex.Length; i += 3)
{
var v1 = veclist[trianglesindex[i]];
var v2 = veclist[trianglesindex[i + 1]];
var v3 = veclist[trianglesindex[i + 2]];
drawTexture2d.DrawLine((int)v1.x, (int)v1.y, (int)v2.x, (int)v2.y, trianglec);
drawTexture2d.DrawLine((int)v2.x, (int)v2.y, (int)v3.x, (int)v3.y, trianglec);
drawTexture2d.DrawLine((int)v3.x, (int)v3.y, (int)v1.x, (int)v1.y, trianglec);
}
}
drawTexture2d.DrawCircle((int)veclist[0].x, (int)veclist[0].y, (int)r, c);
if (drawsurflines){
for(var i=1;i<veclist.Count;i++){
var v1 = veclist[i-1];
var v2 = veclist[i];
drawTexture2d.DrawLine((int)v1.x,(int)v1.y,(int)v2.x,(int)v2.y,c);
drawTexture2d.DrawCircle((int)v2.x,(int)v2.y,(int)r,c);
}
if (closed)
{
drawTexture2d.DrawLine((int)veclist[0].x, (int)veclist[0].y, (int)veclist[veclist.Count - 1].x, (int)veclist[veclist.Count - 1].y, c);
}
}
}
drawTexture2d.End();
return texture;
}
public void Clear(){
//veclist = new List<Vector2>();
texture = new Texture2D(canvasSize, canvasSize, TextureFormat.RGBA32, false);
drawTexture2d = new DrawTexture2D();
veclist.Clear();
trianglesindex = new int[]{};
drawtriangles = false;
closed = false;
}
public void ReleaseSelect(){
selectindex = -1;
}
public int Point(Vector2 vec){
for (var i = 0; i < veclist.Count; i++)
{
if (circleIn(vec, veclist[i], r))
{
return i;
}
}
return -1;
}
public void Move(Vector2 vec)
{
if(selectindex == -1) return;
veclist[selectindex] = vec;
}
public bool Select(Vector2 vec){
selectindex = -1;
for(var i=0;i<veclist.Count;i++){
if(circleIn(vec,veclist[i],r)){
selectindex = i;
return true;
}
}
return false;
}
public bool Add(Vector2 vec){
bool ret = false;
for (var i = 0; i < veclist.Count; i++)
{
if (circleIn(vec, veclist[i], r))
{
//もし追加しようとしてるvecがveclist内の点付近の場合は追加しない
//だたし閉じる処理のveclist[0]のクリックは調べない
if(closed) return false;
}
}
if(closed){
for (var i = 1; i < veclist.Count; i++)
{
if(lineIn(vec,veclist[i-1],veclist[i],r)){
veclist.Insert(i,vec);
ret = true;
break;
}
}
if (lineIn(vec, veclist[0], veclist[veclist.Count - 1], r))
{
veclist.Insert(0, vec);
ret = true;
}
}else{
if (veclist.Count > 2)
{
if (circleIn(veclist[0], vec, r))
{
//閉じるための処理
closed = true;
}
else
{
//追加する
veclist.Add(vec);
ret = true;
}
}
else
{
veclist.Add(vec);
ret = true;
}
}
return ret;
}
public void Remove(Vector2 vec){
if(veclist.Count <= 3){
return;
}
for(var i=0;i<veclist.Count;i++){
if (circleIn(veclist[i], vec, r))
{
veclist.RemoveAt(i);
break;
}
}
}
/// <summary>
/// 線分v1-v2上(太さ2*r)にpが存在するか
/// 点v1,v2付近ではfalseになる部分がある
/// </summary>
/// <param name="p"></param>
/// <param name="v1"></param>
/// <param name="v2"></param>
/// <param name="r"></param>
/// <returns></returns>
private static bool lineIn(Vector2 p,Vector2 v1,Vector2 v2,float r){
float a = v2.x - v1.x;
float b = v2.y - v1.y;
float a2 = a * a;
float b2 = b * b;
float r2 = a2 + b2;
float tt = -(a*(v1.x-p.x)+b*(v1.y-p.y));
float d2;
if(tt < 0){
d2 = (v1.x-p.x)*(v1.x-p.x) + (v1.y-p.y)*(v1.y-p.y);
}
if(tt > r2){
d2 = (v2.x-p.x)*(v2.x-p.x) + (v2.y-p.y)*(v2.y-p.y);
}
float f1 = a*(v1.y-p.y)-b*(v1.x-p.x);
d2 = (f1*f1)/r2;
if (d2<=r && dist2(v1,p) < r2 && dist2(v2,p) < r2){
return true;
}else{
return false;
}
}
/// <summary>
/// 2点間の距離の2乗
/// </summary>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <returns></returns>
private static float dist2(Vector2 p1,Vector2 p2){
float c =(p2.x - p1.x);
float d =(p2.y - p1.y);
return c*c + d*d;
}
private static bool circleIn(Vector2 mousevec, Vector2 plotvec, float r)
{
if (Math.Pow(mousevec.x - plotvec.x, 2) + Math.Pow(mousevec.y - plotvec.y, 2) <= r * r)
{
return true;
}
else
{
return false;
}
}
}
public Material mat1;
Texture2D texture = null;
Texture2D guitexture = null;
private int canvasOffsetX = 240;
private int canvasOffsetY = 20;
private int canvasSize = 500;
private int selGridInt = 0;
private Color pathColor = new Color(0.5f,0.5f,0.5f);
private PathTool pt;
private int hoverindex = -1;
private bool drawTriangles;
private bool drawSurflines;
enum ToolType{
AddEdit = 0,Remove = 1
}
private string[] selCaptions = new string[] {
"Add/Edit",
"Remove",
};
[MenuItem("Window/BroochEditor")]
private static void Create()
{
// 生成
var window = GetWindow<BroochEditor>("Brooch Editor");
window.minSize = new Vector2(320, 320);
window.Init();
}
private void Init(){
pt = new PathTool(canvasSize, 5,pathColor);
mat1 = new Material(Shader.Find("Unlit/Transparent"));
}
void OnInspectorUpdate()
{
if (EditorWindow.focusedWindow == this &&
EditorWindow.mouseOverWindow == this)
{
this.Repaint();
}
}
public static void SetTextureImporterFormat(Texture2D texture, bool isReadable)
{
if (texture.isReadable == isReadable){
return;
}
string assetPath = AssetDatabase.GetAssetPath(texture);
var tImporter = AssetImporter.GetAtPath(assetPath) as TextureImporter;
if (tImporter != null)
{
//tImporter.textureType = TextureImporterType.Default;
tImporter.isReadable = isReadable;
AssetDatabase.ImportAsset(assetPath);
AssetDatabase.Refresh();
}
}
private void canvasClear(){
pt.Clear();
}
private void CreateMesh(){
pt.triangles();
pt.DrawTriangles = true;
}
void OnGUI()
{
Event e = Event.current;
var cmp = new Vector2(e.mousePosition.x - canvasOffsetX,e.mousePosition.y - canvasOffsetY);
EditorGUILayout.BeginHorizontal(GUILayout.MaxWidth(200));{
EditorGUILayout.BeginVertical(GUI.skin.box);{
EditorGUILayout.BeginVertical(GUI.skin.box);
{
var mp = e.mousePosition;
EditorGUILayout.LabelField("x:" + mp.x + " y:" + mp.y + " index:"+ hoverindex);
// if (e.type == EventType.MouseDown){
// Debug.Log(mp);
// }
drawTriangles = EditorGUILayout.Toggle("drawTriangles",drawTriangles);
drawSurflines = EditorGUILayout.Toggle("drawSurflines",drawSurflines);
pt.DrawTriangles = drawTriangles;
pt.Drawsurflines = drawSurflines;
}
EditorGUILayout.EndVertical();
EditorGUILayout.BeginVertical(GUI.skin.box);{
EditorGUI.BeginChangeCheck();
texture = EditorGUILayout.ObjectField("Reference Texture:", texture, typeof(Texture), false) as Texture2D;
if(EditorGUI.EndChangeCheck()){
Debug.Log("ChangeTexture");
if (texture != null){
//SetTextureImporterFormat(texture,true);
}
}
}EditorGUILayout.EndVertical();
EditorGUILayout.BeginVertical(GUI.skin.box);{
EditorGUILayout.LabelField("Tools",EditorStyles.boldLabel);
selGridInt = GUILayout.SelectionGrid(selGridInt, selCaptions, 1, EditorStyles.radioButton);
EditorGUI.BeginChangeCheck();
pathColor = EditorGUILayout.ColorField("Path Color", pathColor);
if(EditorGUI.EndChangeCheck()){
pt.PathColor = pathColor;
guitexture = pt.canvas();
}
}EditorGUILayout.EndVertical();
// EditorGUILayout.BeginVertical(GUI.skin.box);{
// GUILayout.Label("Base Settings", EditorStyles.boldLabel);
// // myString = EditorGUILayout.TextField("Text Field", myString);
// }EditorGUILayout.EndVertical();
GUILayout.Label("Menu",EditorStyles.boldLabel);
if(GUILayout.Button("Clear")){
canvasClear();
guitexture = pt.canvas();
}
EditorGUILayout.Space();
EditorGUI.BeginDisabledGroup(!(pt.Closed && texture!=null));{
if (GUILayout.Button("Create"))
{
CreateMesh();
}
}EditorGUI.EndDisabledGroup();
}EditorGUILayout.EndVertical();
}
if (texture != null)
{
EditorGUI.DrawPreviewTexture(new Rect(240,20, 500, 500),texture,null,ScaleMode.StretchToFill,1,-1,UnityEngine.Rendering.ColorWriteMask.All);
}
if (0 < cmp.x && cmp.x < canvasSize && 0 < cmp.y && cmp.y < canvasSize)
{
guitexture = CanvasBehaviour(e, cmp);
}
if(guitexture != null){
EditorGUI.DrawPreviewTexture(new Rect(240,20,500,500),guitexture,mat1,ScaleMode.StretchToFill,0,-1,UnityEngine.Rendering.ColorWriteMask.All);
}
}
private Texture2D CanvasBehaviour(Event e,Vector2 canvasMousePosition){
// if((ToolType)selGridInt==ToolType.Add){
// switch(e.type){
// case EventType.MouseDown:
// pt.Add(canvasMousePosition);
// break;
// }
// }
hoverindex = pt.Point(canvasMousePosition);
if((ToolType)selGridInt==ToolType.Remove){
switch(e.type){
case EventType.MouseDown:
pt.Remove(canvasMousePosition);
break;
}
}else if((ToolType)selGridInt==ToolType.AddEdit){
switch(e.type){
case EventType.MouseDown:
if(!pt.Add(canvasMousePosition)){
pt.Select(canvasMousePosition);
}
break;
case EventType.MouseDrag:
pt.Move(canvasMousePosition);
break;
case EventType.MouseUp:
pt.ReleaseSelect();
break;
}
}
pt.Point(canvasMousePosition);
return pt.canvas();
}
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment