左边的都是最近一个月买的,几乎全是下半年买的,从这点看来,上下半年的工作节奏的确不太一样。
本来想回次天津,被父母以雾太大,和重感冒别传染为理由拦住了。无形中多了一天的空闲时间,正好可以写写年终的总结了。
最近一个月,快成前端工程师了。
自从开始用BS解决现在的一些问题,就在补各种前端知识,从我买书的节奏就能看出来我对这个问题了解的深入程度。
第一次——ASP.NET入门经典,这时还没意识到前端的重要性,觉得用默认的模板,弄弄后端就OK了。结果这本真的是入门经典,介绍了WebForm,MVC,WebAPI,但是并没有结构性的深入。
第二次——ASP.NET MVC 5高级编程(第5版) & HTML5+CSS3从入门到精通,时隔一周发现已经用MVC就继续用下去,而且也算好学,要单独买一本介绍MVC实现的。另外在用Razor写View的时候,如何让页面看起来不这么难看也需要学习一些css的知识,还要补补HTML的知识。结果后端的问题基本上是解决了,但是一些功能又必须由前端实现(jQuery),而且那本HTML5+CSS3真的是只介绍HTML5和CSS3,完全没讲我想知道的……。
第三次——和第二次隔了10天,这次基本把问题都解决了,首先是Bootstrap入门经典,ASPNET?MVC5集成的是Bootstrap 3.0,实际上了解css是必要的,但是并不能解决我对排版的需求,直接看这个更方便……然后是Web设计与前端开发秘籍:HTML CSS JavaScript jQuery 构建网站,二本套装,讲这四个东西,这是我最近买的书了印刷排版最良心的,如果时间够,从头到尾看下来估计都不会有什么问题,和看网页+PPT效果一样。最后还买了本精通Python网络爬虫:核心技术、框架与项目实战,有些数据要从内网的网页上爬下来,为了图省事准备学学scrapy。
最近几个礼拜弄这些自己不擅长的东西,压力也比较大,也解决了不少问题,元旦过后有时间再整理一下解决的过程,纪录下来。
起因:要上线一个内部的小系统,于是就动手实现,不过以前属于纯玩票,现在毕竟要实际用的东西,认证就要做了,后期要把认证绑到公司域的AD上,但是现在没有权限的情况下,内部测试只能用本地的认证系统了。
开始在完全搞不懂MVC这部分的情况下,想得蛮简单,小范围测试只需要几个人,手动创建完帐号字典,连数据库都不用,直接明文一验证就完了。结果发现框架里已经提供了比较完事的认证系统,就直接用吧,但是不会用……
经过一些时间的搜索,先记录一下。
首先依赖数据库,会使用LocalDB在App_data目录下生成一个mdf的LocalDB文件,一共5张表,用户的,用户组的,映射关系什么的,开始可以先不管。
在创建完框架直接调试,可以进入注册页面,先注册一个帐号,会要求用邮箱,密码也有复杂性要求,这部分如果要修改可以在App_Start\IdentityConfig.cs文件里修改PasswordValidator。
创建完,会发现本地数据库的表AspNetUsers里会多一个用户。当然密码不会用明文存……
然后我的需求1是帐号密码可以由我一个人创建,2是其他人可以登陆,但是不能创建新帐户。
尝试1:直接修改数据库,添加条目,发现AspNetUsers表里的ID是hash值,手动创建其实修改成数字应该也可以,不过显得不专业,放弃。
尝试2:注册、注销,循环注册多个帐户,太傻,放弃。
尝试3:登陆完注册导航按钮消失,直接输入url可以不?可以……这个办法已经可以了,偷懒的话可以把注册代码的注册成功后登陆注销,弄完帐户后再恢复。就下面这行
await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);
需求1解决,需求2搜了点文章,解决这个问题的同时让我对MVC三个层次的工作方式又理解了一些。
解决办法:
// // POST: /Account/Register [HttpPost] //[AllowAnonymous] [Authorize(Roles = "Admins")] [ValidateAntiForgeryToken] public async Task<ActionResult> Register(RegisterViewModel model)
这时候如果调试,会发现所有人都无法访问这个界面,会提示你登陆,然后就看不到了。查了下Account下的几个代码,好像没有集成用户组管理的页面和代码。于是在数据库里手动添加。
在数据库的AspNetRoles表里添加ID为1,Name为Admins的条目,以及ID为2,Name为Users的条目。
在数据库的AspNetUserRoles表里,将刚注册的用户的ID(Hash值)添加到这里的UserID,RoleID为1,就可以设定这个用户的Roles为Admin了。添加的其他用户可以手动分配为2(Users),或者像我一样没需求的,可以不分配。
通过这个问题的搜索学习,还了解到了一些MVC的设计思路,之前自己瞎玩的时候搞得一团糟,几乎把所有的代码都放到了View层,虽然也尝试把数据结构放到了Models,但是还是耦合到一团球。
我之前理解路由(当然是错误的理解),Control层只是起到转发Action的作用,并实现一些数据处理,而且这些数据处理都是手写的,但是在研究注册页的注册按钮如何工作时,我凌乱了,半天没理解怎么弄的,找到了一个stackoverflow的高分回答,解释了一下Model和Control的Binding关系,读了两遍,再回去看代码,果然就明白了。
模型基本上只提供了一个数据结构,而且基本只是数据的值,不包含函数(方法),View在提交Post的时候,调用的是同一个路由,所以开始看的时候莫名的觉得这个页面既显示注册界面,又完成了注册功能。然后找到Controller的时候,虽然明白了Controller如何完成功能,但是又不明白这些数据结构是如何传递的。相对的,也不明白Model是如何在这里面起作用的。
这些能活下来的框架和设计,都是相对简单且实用的,理解起来其实并不是太困难,但是需要一些点拨和经验。
暗黑3的机制现在很搞笑,很多Build要依靠大量高频率的点击来实现,所以导致了鼠标宏的出现,而且官方态度很暧昧,从来不承认这种辅助的合理性,也从来没封禁。
我了解使用这个东西的风险,所以自己写了个类似的东西,分享出来,如果能帮助到有类似需要的人最好,不过请使用的人自己承担风险。
界面就是三个时间间隔,后台是两个快捷键,按Ctrl+B一次,开始无限循环按3,再按Ctrl+3一次,停止无限循环,这个我用在吞噬技能上。对应界面的时间间隔3。注:开始想用Ctrl+3作为快捷键,但是这意味着在连续按3的情况下,只要按了Ctrl键,就等于按了Ctrl+3,就停下来了,所以一直纳闷为什么一放死地吞噬就掉了……Orz
另外一个快捷键是Alt+2,表现为按一次2,然后无限按1,按1的频率间隔是时间间隔1,整个时间持续为时间间隔2。这个对应为死地(设为2号技能)10秒里,不停地按1(尸矛)。
最近不是特别忙,心态上的。
最近2个月没有一个礼拜不出差的,每月基本都有14、5天不在家,老婆怀孕5、6个月心情也不是很平稳,又到了年底事情多的时候,按理应该挺忙挺慌,但是就是没有以前急迫的感觉了。
最主要的改变应该是自己吧,首先是工作和生活尽量分开,出差回来后的晚上和周末绝对不要坐电脑前,无论是工作还是打游戏放松。只要陪老婆的绝对时间够了,无论是看电视逛街还是发呆,结果都不差。
然后就是工作,自己安排工作不要安排或承诺2周以上或以后的事情,领导安排的工作,以及认为重要的事情,订好时间计划,严格保证执行。日常工作做好,如有时间来不及,做好解释工作,寻找解决方案,不能简单的拒绝。对于份外工作,和甩锅,不积极,不主动,不反应。
加班,首先周六日没有以前整天干活的情况发生了,有紧急情况还会忙一会,或者老婆自己有事的时候也能抽空干点活。每天的8小时以外,如果有事情需要做,我要先反思一下为什么今天需要加班,是我耽误了白天的进度,还是被其他事情干扰了,干扰项是否是足够重要的?一定需要加班解决,还是可以安排到明天。总之,加班是救急不救忙。
年轻的时候,除了大四那一年由于考研工作都不顺,比较灰心,其他的时间都是不可一世,没什么本事还是牛逼哄哄,那时候念叨这句话,就是我很牛逼,所以我就这么着,管其他人怎么着。
但这次不是,刚才突然有点想法,但是又很抽象,就想着怎么用语言总结一下,然后就出来这么一句。
起因是某个同事,因为某个事情找我支持,还有一个礼拜就要投标了,我却决定劝他放弃这个项目。当然问题出在我身上,我一直没有给出绝对明确的“放弃吧”,直到今天。我总是习惯于把人和事情结合到一起看,假设每个环节都有强大的逻辑处理和异常处理功能,就像组织良好的代码一样,但是实际上不是的,销售不会因为一些理性的判断就放弃项目,技术人员也不会因为项目很重要就冒风险,就算有,这样的人也会逐渐被淘汰掉,好的流程不会让问题在流转的过程中无声的消失掉,而是通过各种触发条件被激活,再由有关键权限的人做判断,做判断的方法和态度也就是企业文化了。
现在反思一下,当初虽然给出了No,但是没有力度,应该让销售觉得,如果不找领导压我,我就不会改变主意,这也就是把风险和问题,提前暴露,并交由足够权限的人妥善处理。
我现在还没有找准确这个度,这个风格,也就是我还没有找准现在的路,当然只是指这份工作,这个岗位上的,一旦找准了,就要坚持下去,冲突也好,愉快地合作也好,必须是符合我的工作范围,符合我的屁股的位置的。
离新年春节还有三四个月,不过公司级别的财政年度已经结束了,对于工作上,10月已经是新的一年了。9月份真是忙到昏天黑地,看看8月份的blog上还有闲暇研究一下解析几何,这次更新就只能等到国庆假期了。
这一两个月,感觉自己的变化非常大,和过去2年的动荡有关系,终于能看到一个相对稳定的阶段,也就有了总结和展望的前提。虽然总结的时候,看到的更多是自己愚蠢的一面,不过no pain no gain,如果吸取教训更重要,32岁,虽然已经不年轻,但是还有时间,还有机会。
抛去楞头青和犯得蠢不提,最近2年积累太少,一是input少,二是output少,过去做技术工作,遇到什么问题就学什么,同事和客户问什么,就说什么,输入输出都有。现在做两边不靠的工作,需要学什么要自己去总结,一些经验如何归纳也要自己抽空做,实际上无论输入输出都没怎么弄,就会有种虚浮的感觉。
还有时间管理,一直有个想法,要把每天和每周的工作提前计划好,然后严格执行,但是一直没有成功。新财年,还是想要试一试,目前看来,负责的东西有点多,按照之前的经验,一个人不可能干完,对于完全没可能的?事情,只能想办法尽可能地提高效率,尽量完成度高一些,另外一方面,完不成的情况下,也要有足够的取舍,以及证据说明自己尽力了。
业余时间,想多看看书,过去两年一直在看工具书和网络小说,17年底,找一些严肃小说看吧,看点苦大仇深的调节一下,总看爽文也不行。
这个库也只是更新到0.4,而且更新的非常慢,感觉项目基本停滞。
这次主要的目的之一也是锻炼自己,那么参考代码重新实现也对我自己更有利。
里面的一些结构非常值得参考,一些函数可以直接拿来用,不过这个库还是像通常能见到的库的一样是纯几何的,而不是我想实现的测量上的,所以还是要保留自己的一些思路。
另外就是目前在补回归分析的课,还有解析几何的课(高数下)。最近要花一些时间在这些基础上,代码要少写一些。
需求是,根据坐标系的两个轴(向量),求出另外一个轴,由于我高数下(解析几何)不及格,所以一直没有想起来对应的定义。之前自己解决的办法是,求出这两个向量构造的平面,然后平面的法向就是解。
今天在写我的GDTGeometry库时又遇到这个问题,这次先去翻了翻新买的高数下教材,发现这就是向量叉积的定义,可惜当初并没有记牢。于是想直接在MathNet库里找,结果里面只有multiply(点积)和pointwise-multiply(逐点乘积),又特意找了下翻译叉积是Cross-Multiply,我觉得像基础课的很多定义,应该加上英文注释。
搜了下mathnet cross multiply,找到了stack overflow上的一篇问答,这个函数在Iridium子库里实现,不过这个库已经太监了,而且2012年就太监了,到现在还没有整合到numerics(主库里),简直是无语,我很想说这么个简单的功能我commit一个patch吧,不过仔细想了下,问题应该出在叉积通常只用在3维空间里,低维和高维的推广好像都有点问题。
overflow上的回答里已经有了我要的代码:
using DLA = MathNet.Numerics.LinearAlgebra.Double; public static DLA.Vector Cross(DLA.Vector left, DLA.Vector right) { if ((left.Count != 3 || right.Count != 3)) { string message = "Vectors must have a length of 3."; throw new Exception(message); } DLA.Vector result = new DLA.DenseVector(3); result[0] = left[1] * right[2] - left[2] * right[1]; result[1] = -left[0] * right[2] + left[2] * right[0]; result[2] = left[0] * right[1] - left[1] * right[0]; return result; }
针对三维向量。
推导在高数下教材上有,和我一样数学差的可以去查。结论是
\[c=a\times b=\begin{vmatrix}i&j&k\\a_x&a_y&a_z\\b_x&b_y&b_z\end{vmatrix}\]
所以
\[i_c=\begin{vmatrix}a_y&a_z\\b_y&b_z\end{vmatrix}\]
\[j_c=-\begin{vmatrix}a_x&a_z\\b_x&b_z\end{vmatrix}\]
\[k_c=\begin{vmatrix}a_x&a_y\\b_x&b_y\end{vmatrix}\]
开始想把这部分写成DenseVector的扩展函数,后来想想既然只能用在3维向量上,就放到自己的Vector(三维)类里了。
实际上坐标测量里研究的变换只有两种,一种是旋转,上一篇文章那样的,另外一种就是平移。
基本坐标系(自然基)
\(O=\begin{bmatrix}1&0&0&0\\0&1&0&0\\0&0&1&0\\0&0&0&1\end{bmatrix}\)
点P
\(\begin{bmatrix}4\\1\\0\\1\end{bmatrix}\)
代表坐标系的矩阵由\(3*3\)变成了\(4*4\),坐标的列向量也由3个元素加了一个1。
虽然介绍仿射变换方法的文章很多,但是原理性的介绍没找到,这种表达形式意味着空间由3维变成了4维,虽然第四维的坐标永远是1,如果有哪位朋友有相关文章提供阅读,感激不尽。
先将坐标系延基本坐标系平移\([0,-3,0]\),再延Z轴旋转45度,在新坐标系
\(A=\begin{bmatrix}\frac{\sqrt{2}}{2}&-\frac{\sqrt{2}}{2}&0&0\\\frac{\sqrt{2}}{2}&\frac{\sqrt{2}}{2}&0&-3\\0&0&1&0\\0&0&0&1\end{bmatrix}\)
下的坐标为点
\(P_{A}=\begin{bmatrix}4\sqrt{2}\\0\\0\\1\end{bmatrix}\)
满足
\[OP=AP_{A}\]
新坐标系的过渡矩阵(相对于自然基)和之前的相比,加了一列(基的个数+1),是新坐标系0点在基本坐标系的坐标值。
将基本坐标系平移\([-10,-10,-10]\)再延Y轴旋转90度。
\(B=\begin{bmatrix}0&0&1&-10\\0&1&0&-10\\-1&0&0&-10\\0&0&0&1\end{bmatrix}\)
在B坐标系下P点坐标
\(P_{B}=B^{-1}P=\begin{bmatrix}-10\\11\\14\\1\end{bmatrix}\)
按向量空间的定义,由A到B的过渡矩阵为\(A^{-1}B\)
\(\begin{bmatrix}0&\frac{\sqrt{2}}{2}&\frac{\sqrt{2}}{2}&-12.0208\\0&\frac{\sqrt{2}}{2}&-\frac{\sqrt{2}}{2}&2.12132\\-1&0&0&-10\\0&0&0&1\end{bmatrix}\)
B到A的过渡矩阵\(Q^{-1}\)为
\(\begin{bmatrix}0&0&-1&-10\\\frac{\sqrt{2}}{2}&\frac{\sqrt{2}}{2}&0&7\\\frac{\sqrt{2}}{2}&-\frac{\sqrt{2}}{2}&0&10\\0&0&0&1\end{bmatrix}\)
过渡矩阵的最后一行依旧是\(\begin{bmatrix}0,0,0,1\end{bmatrix}\)
这样点的变换在平移和旋转坐标系的数学关系就整理好了。
在向量空间里,所有的向量表示的都是向量(既有方向,又有长度-大小),这和空间上的点坐标是类似的,但是在测量或者建模时,点还有一个特性就是点的法向(矢量),这个参数只有方向,没有大小(总是单位长度1)。
当坐标系平移时,这个法向是不会随之平移的,只能当坐标系旋转时,法向才会随之旋转。
所以对于法向,坐标的最后一个值永远为0,这样在矩阵变换时,就不会受最后基的最后一个列向量影响了。
算是写完,消耗时间大约是2个晚上加周六周日的业余时间,当然周六周日我都有早上打篮球,周六还去理了发。之所以要把这些很简单的东西整理一遍,而不是把网上现成的材料拷贝保留下来,原因有两个。
1.自己整理一遍,把自以为理解的东西讲一遍,可以排查一下自己是否真正理解了,而且理解的比较全面了。
2.2013年我第一次尝试用C#解决实际的问题,在用代码实现的过程中,发现复杂度比我估计得要多(然后我并没有吸取教训,直到现在我预估依然很差)。而且,虽然矩阵的理论很完备,并不意味着算法是统一的,比如DirectX用行向量,OpenGL用列向量,再比如Math.Net里的DenseMatrix.OfArray构造的矩阵,将数组里的元素按列优先的顺序存入数组。这就导致我在没有足够理论储备的情况下只能用实际数据的测试的方法实现了需要的功能。等到2015年我因为其他事情要扩展这部分代码时,我发现我已经无法修改了。
所以这次借写新库的机会,将理论整理清楚,未来写代码时就不会现一会矩阵乘向量,一会向量乘矩阵,最后乱成一团的情况了。
using System; using MathNet.Numerics.LinearAlgebra.Double; using System.Diagnostics; namespace CoordinateTest { class Program { static void Main(string[] args) { dimension3(); dimension4(); } static void dimension3 () { DenseMatrix O = DenseMatrix.OfArray(new double[,] { {1,0,0 }, {0,1,0 }, {0,0,1 } }); double SqrtTwo = Math.Sqrt(2) / 2.0; DenseMatrix A = DenseMatrix.OfArray(new double[,] { {SqrtTwo,-SqrtTwo,0 }, {SqrtTwo,SqrtTwo,0 }, {0,0,1 } }); DenseMatrix B = DenseMatrix.OfArray(new double[,] { {0,0,1 }, {0,1,0 }, {-1,0,0 } }); DenseVector P = DenseVector.OfArray(new double[] { 1, 1, 0 }); DenseVector PA = A.Inverse() * P as DenseVector; DenseVector PB = B.Inverse() * P as DenseVector; Debug.WriteLine(PA); Debug.WriteLine(PB); } static void dimension4() { DenseMatrix O = DenseMatrix.OfArray(new double[,] { {1,0,0,0 }, {0,1,0,0 }, {0,0,1,0 }, {0,0,0,1 } }); DenseVector P = DenseVector.OfArray(new double[] { 4, 1, 0, 1 }); double SqrtTwo = Math.Sqrt(2) / 2.0; DenseMatrix A = DenseMatrix.OfArray(new double[,] { {SqrtTwo,-SqrtTwo,0,0 }, {SqrtTwo,SqrtTwo,0,-3 }, {0,0,1,0 }, {0,0,0,1 } }); DenseVector PA = DenseVector.OfArray(new double[] { 8* SqrtTwo, 0, 0, 1 }); Debug.WriteLine(A * PA); DenseMatrix B = DenseMatrix.OfArray(new double[,] { {0,0,1,-10 }, {0,1,0,-10 }, {-1,0,0,-10 }, {0,0,0,1 } }); Debug.WriteLine(B.Inverse()); DenseVector PB = B.Inverse() * P as DenseVector; Debug.WriteLine(PB); DenseMatrix Q = A.Inverse() * B as DenseMatrix; Debug.WriteLine(Q); Debug.WriteLine(Q * PB); Debug.WriteLine(A * Q); Debug.WriteLine(Q.Inverse()); //for vector test DenseVector v1 = DenseVector.OfArray(new double[] { SqrtTwo, SqrtTwo, 0, 0 }); Debug.WriteLine(A.Inverse() * v1); } } }
输出为:
DenseVector 3-Double 1.41421 0 0 DenseVector 3-Double 0 1 1 DenseVector 4-Double 4 1 0 1 DenseMatrix 4x4-Double 0 0 -1 -10 0 1 0 10 1 0 0 10 0 0 0 1 DenseVector 4-Double -10 11 14 1 DenseMatrix 4x4-Double 0 0.707107 0.707107 -12.0208 0 0.707107 -0.707107 2.12132 -1 0 0 -10 0 0 0 1 DenseVector 4-Double 5.65685 4.44089E-16 0 1 DenseMatrix 4x4-Double 0 0 1 -10 0 1 0 -10 -1 0 0 -10 0 0 0 1 DenseMatrix 4x4-Double 0 0 -1 -10 0.707107 0.707107 0 7 0.707107 -0.707107 0 10 0 0 0 1 DenseVector 4-Double 1 0 0 0