liuxq / blog Goto Github PK
View Code? Open in Web Editor NEW记录,成长
记录,成长
<<和 >> 操作符不能读写二进制文件
对于二进制存储string,在文本里保存的也是asc11,跟直接存文本差不错
生成dll时对外接口的参数中不要有string(vector等猜测也不行)否则会有删除临时string时堆异常,至少我做的那个例子是这样,不是道是否恒成立。
类导出dll类导出dll类导出dll,最好让导出.h和导入.h共用,否则容易不一致,如果不一致,将会在大部分情况是没有错误的,而在小部分情况(如使用release时)程序崩溃,导致极难找到错误所在。另外程序不要只在debug下跑,很多错误(例如内存泄露)只在release时才会出现
char temp[10][10];
char destTemp[10][10];
memcpy(destTemp,temp,100);
复制的单位是所有元素数(而不是想自己考虑的那样以10个为单位);
10是换行
char a[20] = {'a','b','c',10,'a','b'};
cout<< a<<endl;
显示结果是:
abc
ab
ifstream aa;aa.get();可以得到字符,包括换行符
while (inFileReader.peek() == ' ' || inFileReader.peek() == '\t')
{
inFileReader.ignore();
}
用于去掉空格
dfs可以通过bool数组保存状态,对处理过得状态作为临界条件
可以使用筛法计算n内的所有素数,时空复杂度都为o(n)
std::string 和string不一样,比如:一个支持“==”,一个不支持
注意注意在c++中如果不导入math.h,则abs(double)会使double截尾。usaco中不支持abs(double)只支持int,导入math.h也不行,
ofstream 写文件时到最后close或flush或超过了缓存才会真的写到文件中,或者调用了endl
struct char4
{
unsigned char a:4;
unsigned char b:4;
}
可以这样来节省空间
vector::resize(n,v) .在0-min(n,size)之间的值是不变的,后面的值赋为v
dll还需要lib作为引导库,其中记录了函数头信息,但是在发布之后lib信息都被链接到了exe中,就不需要在发布文件件中再放入lib文件了
vtk小tip
actor 如果new了并且render->addActor了,就要render->removeactor,但是只new一次actor,后面的render->addActor就不需要render->removeactor了
sgrid->GetPointData()->GetScalars()->GetRange()才能获得当前scalar范围
而sgrid->GetPointData()->GetArray(int)->GetRange()获取的范围不对
vtkdataArray 要调用getRange之后才会计算范围
glsl
uniform sampler2D tex;定义的变量如果没有在代码中引用,则不会成功定义,不会放到表中
opengl中的quad点的顺序是需要顺时针的
glColor3ub要记得加u不然很悲剧
需要设置项目依赖项,否则其他项目中lib是不可见的
动态链接库版本:
/MD Multithreaded DLL 使用导入库MSVCRT.LIB
/MDd Debug Multithreaded DLL 使用导入库MSVCRTD.LIB
静态库版本:
/ML Single-Threaded 使用静态库LIBC.LIB
/MLd Debug Single-Threaded 使用静态库LIBCD.LIB
/MT Multithreaded 使用静态库LIBCMT.LIB
/MTd Debug Multithreaded 使用静态库LIBCMTD.LIB
使用一个开源库,先看生成库时使用的运行时库,在调用时必须一致
openGL在使用拾取时,注意鼠标的y值使用viewport[3]来减,此时viewport中的高和宽不可以归一,因为归一后高可能不再是屏幕的高,此时有两种应对变形的解决方案:1.使用glOrtho对应当前窗口的宽和高,2.viewport继续归一,只是不再使用viewport[3]来减,而是使用当前窗口的高来减
gldisableClientState(GL_COLOR_ARRAY);再glEnableClientState(GL_COLOR_ARRAY);时必须重新指定颜色数组,其他数组也一样
glewInit();必须先调用才能使用glew库里的东西
在写入帧缓存时不需要glEnable(texture_2D);想想也不需要,前者是要把纹理帖到表面上
半透明要记得先绘制不透明的,再绘制半透明的
帧缓冲对象需要给它指定深度纹理,才能向其中写入带深度测试的结果,否则深度测试没法进行
纹理一旦取出来了(bind)就不可以再往里写,除非重启帧缓存
GL_TEXTURE_RECTANGLE需要注意,sample时,坐标并不归一,而GL_TEXTURE_2D的坐标是归一的
openGL的透视投影是把坐标按照ortho的参数使之在三个方向都规约到-1-1之间,而在范围之外的点被裁减掉,之后再由viewport参数进行光栅化。
向量叉乘的左右手定则按照坐标系而变换,是左手坐标系就用左手,否则就用右手
旋转矩阵的推导,先把p分解为x和y方向的两个向量的和,之后旋转x和y向量,再做加法
vector多线程时,如果一个线程在写,另一个同时读的条件是集合的元素是小类型如(double)而如果存着vector等大数据,则不可以同时读写
集合中pushback元素时,集合中该元素的地址跟原来元素的地址是不同的
QSplitter当delete时,里面放置的widget也一起delete
定义结构体的时候,成员最好能从大到小来定义,那样能相对的省空间
CMyString& CMyString::operator =(const CMyString &str) {
m_pData = new char[strlen(str.m_pData) + 1];
strcpy(m_pData, str.m_pData);
return *this;
}这样是可以的,虽然m_pData是私有的,但是可以访问,因为跟自己的同一个类
顺序表删除多个元素时,可以通过类似partition的**,遍历一次删除完全
signal里面的函数参数只能写类型,不能写变量名字
Vc6.0中的fstream必须要加ios::out才能创建文件
public static void DockEditorWindow(EditorWindow parent, EditorWindow child)
{
Vector2 screenPoint = parent.position.min + new Vector2(parent.position.width * .9f, 100f);
Assembly assembly = typeof(UnityEditor.EditorWindow).Assembly;
Type ew = assembly.GetType("UnityEditor.EditorWindow");
Type da = assembly.GetType("UnityEditor.DockArea");
Type sv = assembly.GetType("UnityEditor.SplitView");
var tp = ew.GetField("m_Parent", BindingFlags.NonPublic | BindingFlags.Instance);
var opArea = tp.GetValue(parent);
var ocArea = tp.GetValue(child);
var tview = da.GetProperty("parent", BindingFlags.Public | BindingFlags.Instance);
var oview = tview.GetValue(opArea, null);
var tDragOver = sv.GetMethod("DragOver", BindingFlags.Public | BindingFlags.Instance);
var oDropInfo = tDragOver.Invoke(oview, new object[] { child, screenPoint });
var tDockArea_ = da.GetField("s_OriginalDragSource", BindingFlags.NonPublic | BindingFlags.Static);
tDockArea_.SetValue(null, ocArea);
var tPerformDrop = sv.GetMethod("PerformDrop", BindingFlags.Public | BindingFlags.Instance);
tPerformDrop.Invoke(oview, new object[] { child, oDropInfo, null });
}
由于使用的拖拽模拟,有一些限制,比如窗口比例如果想1:1的话,需要先把第一个窗口的大小调到最小(我用的100,100),然后再停靠一个窗口时,两个窗口就会一样大,停靠之后再把窗口放大到想要的尺寸可以保持1:1的比例。
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Reflection;
using Type = System.Type;
public static class LayoutUtility
{
private static MethodInfo _miLoadWindowLayout;
private static MethodInfo _miSaveWindowLayout;
private static MethodInfo _miReloadWindowLayoutMenu;
private static bool _available;
static LayoutUtility()
{
Type tyWindowLayout = Type.GetType("UnityEditor.WindowLayout,UnityEditor");
Type tyEditorUtility = Type.GetType("UnityEditor.EditorUtility,UnityEditor");
Type tyInternalEditorUtility = Type.GetType("UnityEditorInternal.InternalEditorUtility,UnityEditor");
if (tyWindowLayout != null && tyEditorUtility != null && tyInternalEditorUtility != null)
{
_miLoadWindowLayout = tyWindowLayout.GetMethod("LoadWindowLayout", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string), typeof(bool) }, null);
_miSaveWindowLayout = tyWindowLayout.GetMethod("SaveWindowLayout", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string) }, null);
_miReloadWindowLayoutMenu = tyInternalEditorUtility.GetMethod("ReloadWindowLayoutMenu", BindingFlags.Public | BindingFlags.Static);
if (_miLoadWindowLayout == null || _miSaveWindowLayout == null || _miReloadWindowLayoutMenu == null)
return;
_available = true;
}
}
public static bool IsAvailable
{
get { return _available; }
}
public static void SaveLayoutToAsset(string assetPath)
{
SaveLayout(Path.Combine(Directory.GetCurrentDirectory(), assetPath));
}
public static void LoadLayoutFromAsset(string assetPath)
{
if (_miLoadWindowLayout != null)
{
string path = Path.Combine(Directory.GetCurrentDirectory(), assetPath);
_miLoadWindowLayout.Invoke(null, new object[] { path , false });
}
}
public static void SaveLayout(string path)
{
if (_miSaveWindowLayout != null)
_miSaveWindowLayout.Invoke(null, new object[] { path });
}
}
我们可以拖出来一个设计好的布局然后使用SaveLayoutToAsset保存布局到自定义路径,然后把布局文件上传到svn,策划update之后就可以直接通过菜单调用LoadLayoutFromAsset来加载布局啦
private string path = "Assets/Resources/Model";
private void OnPreprocessModel()
{
ModelImporter modelim = assetImporter as ModelImporter;
if (modelim == null)
return;
//model目录下带动作的fbx不要导入材质
if (assetPath.Contains(path))
{
if (modelim.importAnimation && modelim.clipAnimations.Length > 0 && modelim.importMaterials)
{
modelim.importMaterials = false;
}
}
}
private void OnAssignMaterialModel(Material material, Renderer renderer)
{
//提示材质有问题的fbx
if (material.mainTexture == null)
{
ModelImporter modelim = assetImporter as ModelImporter;
if (modelim != null && modelim.importMaterials == true)
{
EditorUtility.DisplayDialog("fbx材质问题", "模型:" + assetPath + "的材质mainTexture是空!请检查模型", "ok");
}
}
}
导入其他资源也同理。
今年初买房凑首付,办理信用贷的时候自己查了一下征信记录,发现一笔已经三个月逾期未还的贷款记录,放贷方是平安易贷。
打电话问发现是有人冒用的身份证在三月份办理了贷款。过程是那个人先用我的身份证在上海的农业银行办理了一张银行卡,然后在平安易贷的网络APP上面申请了贷款并放贷。至今已经四个月的逾期。
去了两次派出所,打了无数次电话,金融服务办,银监会,市民热线,法务热线,拖了一个月还没有办成。最终妥协,帮骗子还了钱了事。以后一定要看好自己的身份证。
这阵子使用kbengine+unity做了一个游戏。一款mmorpg的移动端网络游戏,采用大世界即时战斗方式。 这个游戏有两个职业,每个职业有三个技能。战士的血量和防御比较强,有近程打击和吸血的技能;法师血少攻击距离远,具有远程攻击和治疗的技能。 左下角是控制手柄,视角会随着人物方向变换,点击视角可以瞬间完成变换,右下角是技能按钮和切换对象按钮用来选择对象并攻击或援助。 点选怪物或者玩家左上角会出现血条和名字,表示已选中,此时点击技能可以对该目标释放,如果目标距离大于技能范围则会自动移动到技能范围内,点击左上角自己的血条可以取消对其他目标的选中,此时释放治疗技能可以治疗自己。
v1.0版,目前有物品系统,装备系统,战斗系统,聊天系统完成
使用kbengine-0.7.0,客户端使用unity5.2
客户端代码地址:https://github.com/liuxq/TestGame/releases
服务端代码地址:https://github.com/liuxq/MyGameServerAssets/releases
在UI上显示模型大致两种方法,一是直接把模型放在UI上渲染,二是另启一个相机(或者commandbuffer)将模型渲染到rendertexture上,然后把rendertexture在UI上当做图片来渲染。
第一种方式由于不利于UI层级管理,大部分项目用的第二种方式。
大体方案是指定一个分辨率的rendertexture,赋给渲染模型的camera,相机clearcolor的a设置成0,这样模型存在的像素上a是大于0的。后续混合到UI背景上时使用One OneMinusSrcalpha的blend就能保证保留实体模型,丢弃原背景。
如上图所示,大体上达到要求,但是细心观察能看到模型的周围有一个轮廓边。轮廓边的产生是由于纹理采样的误差导致的。
在填充一个像素的时候,通过uv坐标去采样纹理,但是uv坐标不一定正好指定在一个像素上,还可能(很可能)指定在四个像素之间,这时候硬件会根据周围四个像素插值出这个坐标的颜色。如下图所示,图中的0和1表示纹理的alpha值,我们把它想象成rendertexture中模型实体和背景的边界,红点表示当前要渲染的像素,此时红点的值应该是0到1之间,blend之后是会渲染出背景颜色的,这个漏出的背景颜色就是我们看到的轮廓边。(比如在渲染模型时用的ClearColor是红色,则轮廓边就是红色的)
想要去掉轮廓边,就需要uv坐标正好取在纹理的像素之上,而不是像素之间。要达到这个要求,需要满足两点:1.渲染像素和输入的纹理像素是一一对应的,也就是渲染的quad的分辨率和输入的rendertexture的分辨率必须一致。2.渲染的每个像素要和输入的纹理像素一一对齐,由于分辨率是一致的,所以只要起始像素对齐,也就都对齐了。
分辨率一致的要求比较简单,需要换算一下渲染的UI上的quad的实际分辨率是多少,然后根据这个分辨率去设置rendertexture的分辨率。
像素对齐的要求可以用一个取巧的方法,把纹理采样的方式从Bilinear改为Point,也就是即使没有对齐,但是会找最近的像素,而不是去相邻四个像素去插值。
ShaderVariants(下文用shader变种描述)是unity中用来合并组织shader的一个方式之一,在shader中的使用类似宏定义。最近项目使用shader变种的时候发现了一些坑,所以做了如下实验和总结。其中前两节是基础部分,看官方文档也可以了解,只是通过实验来加强理解。第三节shader变种的打包是重点描述的,比较重要。
为了做实验,制作shader如下:
Shader "Custom/Color"
{
SubShader
{
Pass
{
Cull Off
ZWrite Off
Lighting Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#pragma shader_feature RED GREEN BLUE
//#pragma shader_feature GREEN
//#pragma shader_feature BLUE
//#pragma multi_compile RED GREEN BLUE
//#pragma multi_compile __ GREEN
struct v2f
{
fixed4 pos : SV_POSITION;
};
v2f vert (appdata_base v)
{
v2f o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
return o;
}
half4 frag(v2f i) : COLOR
{
fixed4 c = fixed4(0,0,0,1);
#ifdef RED
c += fixed4(1,0,0,1);
#endif
#ifdef GREEN
c += fixed4(0,1,0,1);
#endif
#ifdef BLUE
c += fixed4(0,0,1,1);
#endif
return c;
}
ENDCG
}
}
CustomEditor "ColorsGUI"
}
shader的变种数量可以通过shader面板上面查看到,点击Show按钮可以看到都有哪些变种
做如下实验对比shader_feature:
单一行命令
#pragma shader_feature RED
两个变种: __, RED
#pragma shader_feature RED GREEN
两个变种:RED,GREEN
#pragma shader_feature RED GREEN BLUE
三个变种:RED,GREEN,BLUE
#pragma multi_compile RED
一个变种:RED
#pragma multi_compile RED GREEN
两个变种:RED,GREEN
#pragma multi_compile RED GREEN BLUE
三个变种:RED,GREEN,BLUE
分析:shader_feature 和multi_compile 在Keyword 数量大于1时,生成变种的机制是一样的,都是一个keyword一个变种;当keyword只有1个时,shader_feature 会增加一个none变种。再来做个实验:
#pragma shader_feature _ RED
两个变种: none, RED
可见,当shader_feature 的keyword数量是1时不论是否有__符号,都会增加一个空keyword(__),除了这个在生成变种的机制上和multi_compile都是一致的。
多行命令
#pragma multi_compile __ RED
#pragma multi_compile __ GREEN
四个变种:__,RED,GREEN,RED GREEN
分析:多行命令就是单行命令的乘法组合,shader_feature和multi_compile除了单一keyword时是否补__之外,在多行命令中也是一致的。
为了实验shader变种的匹配,做一个方便定义keyword的shader界面,代码如下:
public class ColorsGUI: ShaderGUI {
private static bool bRed = false;
private static bool bGreen = false;
private static bool bBlue = false;
public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
{
// render the default gui
base.OnGUI(materialEditor, properties);
Material targetMat = materialEditor.target as Material;
bRed = Array.IndexOf(targetMat.shaderKeywords, "RED") != -1;
bGreen = Array.IndexOf(targetMat.shaderKeywords, "GREEN") != -1;
bBlue = Array.IndexOf(targetMat.shaderKeywords, "BLUE") != -1;
EditorGUI.BeginChangeCheck();
bRed = EditorGUILayout.Toggle("红", bRed);
bGreen = EditorGUILayout.Toggle("绿", bGreen);
bBlue = EditorGUILayout.Toggle("蓝", bBlue);
if (EditorGUI.EndChangeCheck())
{
if (bRed)
targetMat.EnableKeyword("RED");
else
targetMat.DisableKeyword("RED");
if (bGreen)
targetMat.EnableKeyword("GREEN");
else
targetMat.DisableKeyword("GREEN");
if (bBlue)
targetMat.EnableKeyword("BLUE");
else
targetMat.DisableKeyword("BLUE");
}
}
}
这样,勾选一个颜色,就会enable一个keyword,通过查看结果颜色就能知道匹配到了哪个shader变种,实验如下:
#pragma multi_compile RED GREEN(两个变种:RED, GREEN)
材质keyword为RED : 显示红色(匹配RED)
材质keyword为GREEN : 显示绿色(匹配GREEN)
材质keyword为__ : 显示红色(匹配RED)
材质keyword为RED GREEN: 显示红色(匹配RED)
分析:当keyword存在正好匹配的变种时直接匹配、当keyword不存在匹配变种时取第一个变种
打包的代码如下:
[MenuItem("Assets/Build AssetBundles")]
static void BuildAllAssetBundles()
{
List<AssetBundleBuild> maps = new List<AssetBundleBuild>();
maps.Clear();
//资源打包
string[] files = {
"Assets/ShaderVariants/Resources/red.prefab",
};
AssetBundleBuild build = new AssetBundleBuild();
build.assetBundleName = "ShaderVariantsPrefab";
build.assetNames = files;
maps.Add(build);
string[] file2s = {
"Assets/ShaderVariants/Resources/Shader/Colors.shader",
};
AssetBundleBuild build2 = new AssetBundleBuild();
build2.assetBundleName = "ShaderVariantsShader";
build2.assetNames = file2s;
maps.Add(build2);
BuildAssetBundleOptions options = BuildAssetBundleOptions.DeterministicAssetBundle;
BuildPipeline.BuildAssetBundles("Assets/StreamingAssets", maps.ToArray(), options, BuildTarget.StandaloneWindows);
AssetDatabase.Refresh();
}
读包并实例化的代码如下:
public class BundleLoader : MonoBehaviour
{
void Start () {
load();
}
public void load()
{
StartCoroutine(LoadMainGameObject("file://" + Application.dataPath + "/StreamingAssets/" + "ShaderVariantsShader"));
StartCoroutine(LoadMainGameObject("file://" + Application.dataPath + "/StreamingAssets/" + "ShaderVariantsPrefab"));
}
private IEnumerator LoadMainGameObject(string path)
{
WWW bundle = new WWW(path);
yield return bundle;
if (bundle.url.Contains("ShaderVariantsShader"))
{
//依赖shader包
bundle.assetBundle.LoadAllAssets();
}
else
{
UnityEngine.Object obj = bundle.assetBundle.LoadAsset("assets/ShaderVariants/resources/red.prefab");
Instantiate(obj);
bundle.assetBundle.Unload(false);
}
}
}
为了对比shader_feature 和multi_compile 以及shader依赖和非依赖打包,做如下实验:
#pragma shader_feature RED GREEN BLUE
将选中RED关键字的prefab打包,加载bundle和其中的prefab,显示了红色,此时改变此材质的keyword为GREEN或者BLUE,没有效果
#pragma multi_compile RED GREEN BLUE
将选中RED关键字的prefab打包,加载bundle和其中的prefab,显示了红色,此时改变此材质的keyword为GREEN或者BLUE,可以显示绿色和蓝色
分析:shader_feature声明变种时,打包只会打包被资源引用的keyword变种,multi_compile声明变种时,打包会把所有变种都打进去
#pragma shader_feature RED GREEN BLUE
将选中RED关键字的prefab和shader依赖打包,加载bundle和其中的prefab,显示了异常粉红,任何变种都没有生效
分析:shader_feature标记的shader单独依赖打包时,任何变种都不会打进去,分析原因估计是unity认为单包中shader没有被引用过
unity5中新出的shader_feature可以只将引用过的shader变种打进包里面,听起来很有用,可是大部分项目中为了节省冗余shader的内存,shader都是作为依赖包单独成一包的,此时没有任何shader变种被打进包中;更何况即使shader没有依赖打包,如果计划代码中动态修改shader的变种而不是记录在材质里面,此时也不能用shader_feature。基本上我们的项目中shader_feature可以废弃了。。。
之前做的基于kbengine的一个lua版的mmo小游戏https://github.com/liuxq/StriveGame
有同学提issue希望把插件层独立出来。于是做了逻辑和插件层的解耦,主要代码结构如下:
其中Csharp文件夹中的memoryStream, NetworkInterface, PacketReceiver, PacketSender 都是网络基础类,基本上不会改动热更,并没有lua化;Dbg, Profile, Event, 在lua层都有类似功能,暂时保留c#版本;
KBELuaUtil是lua层需要用到的一些基础函数,还有c#调用lua函数的工具函数指针(为了和tolua解耦,比如如果要用xlua之类的可以传入相应lua插件的调用lua接口)。
其中Lua文件夹中的eventlib.lua和events.lua是lua层的事件结构(event依赖于tolua,如果不用tolua需要修改这里);
其他的lua文件都是同名c#插件的lua化。
为了验证插件的可用性,把kbengine的官方demo做了lua化,包含了所有逻辑:ui,移动,镜头等
游戏截图:
最后附上plugins和demo的github地址:
plugins: https://github.com/liuxq/kbengine_unity3d_lua_plugins
最近美术希望增加地形纹理到8张,希望研究可行性和消耗,于是做了如下测试。
修改4纹理shader到8张纹理,控制图也从一张增加为两张,第一张的RGBA控制前四个纹理,第二章的RGBA控制后四个纹理,然后用ps做出两个控制图,将图分成八份,shader界面和渲染结果如下。
测试机器是macmini,使用的unity4.7,查看profile,测试结果如下。
512512的dxt1 纹理内存大小128k(安卓ect-4bit,苹果pvrtc-4bit)
256256的rgba32 纹理内存大小256k
每个场景增加4个dxt1纹理1个rgba32纹理,每个场景增加内存:768k,如果使用mipmap则增加为1.33倍到1m
1*1米的场景2个drawcall,使用4纹理shader的GPU时间:0.233ms
1*1米的场景2个drawcall,使用8纹理shader的GPU时间:0.464ms
512*512米的场景合并之后13个drawcall,使用4纹理shader的GPU时间:0.805ms
512*512米的场景合并之后13个drawcall,使用8纹理shader的GPU时间:1.274ms
注:这里的512512米用的是1616的网格,共1024个,静态batch过后drawcall达到13
分析8纹理方案的GPU消耗是4纹理的1.6~2倍,由于测试GPU使用的mac,在真机上消耗时间的基数会大大增加。另外测试发现八张贴图的shader如果删掉其中四张,GPU时间消耗不变,因此如果有的图不打算使用8贴图,一定要使用4贴图的shader。
部分低端机型由于没有测试过有可能存在shader不支持或者纹理增多导致的显存不足,存在不支持的风险。
项目目前的主要性能瓶颈在内存,所以内存这块消耗的增加需要其他模块考虑降低消耗。
(比如只有256*256的地图才使用8纹理)
GPU这块由于不是瓶颈,暂时考虑可以接受
兼容问题需要考虑,即如果有的机型不支持8纹理,需要替代方案(比如只使用前四张纹理)
今天在工作上遇到了需要判断一个给定的任意多边形是逆时针还是顺时针的问题。
如果是凸多边形,那看任意两个边的向量做一个叉乘就知道方向了。考虑到有可能是凹多边形,所以我判断了所有相邻的边的向量叉乘,然后取大于一般的方向。具体代码如下:
//判断多边形是逆时针还是顺时针, true:逆时针
bool isAntiClock(vector<Vec2d>& polygon)
{
int sz = polygon.size();
int greaterC = 0, lessC = 0;
for (int i = 0; i < polygon.size(); i++)
{
Vec2d a = polygon[i];
Vec2d b = polygon[(i+1)%sz];
Vec2d c = polygon[(i+2)%sz];
if ((b-a)/(c-b) > 0)
greaterC ++;
else
lessC ++;
}
if (greaterC > lessC)
{
return true;
}
else
return false;
}
之后,我查了一下网上的资料,用的方法更科学高效,是找到多边形最左下角的节点,然后看该节点相邻两个边的向量方向。因为是最左下角,所以一定是一个凸的节点,顿时觉得自己low爆儿了。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.