从正视图中测量长度和角度

目录

前言

  1. 距离公式:已知两点A、B的坐标值:
  2. 余弦定理:已知三个边长:

编程语言的选择

具体算法分析

  1. 步骤
  2. 界面
  3. 操作步骤
  4. 其他要求

根据上面的步骤开始码代码

  1. HTML框架
  2. CSS样式
  3. JavaScript的编写
  4. 定义变量
  5. 选择并显示图片
  6. 获取坐标和画圆的功能函数
  7. Shift快捷键 
  8. 鼠标事件

结语


前言

作为一个机械行业的人,总是会充满好奇,总会想知道一个物体的具体尺寸,还有它们相关角度。

但是有些东西吧,可能是体积巨大,也可能是长相奇特,还可能需要特殊工具才能测量,尤其是出门在外,身上也没啥东西,但还有好奇心,就不是很方便。

以下为两张操作截图:

测量长度
角度测量
长度测量(参照物为两个吉字的间距)

作为一个标准的普通人,手机出门从来不敢离手,尤其是我这种经常喜欢拍照片的,如果拍下了某些事物的视图(禁止拍摄的东西当然是不能去拍了),有了这个照片,只要图片是正视,角度是很容易得到的,如果旁边还有一个知道尺寸的参照物(手掌不就是一个相当好的参照物么),那两点间的实际长度也是可以得到的,这些东西的算法也很简单,中学就学过了。如下:

距离公式:已知两点A、B的坐标值:

L_{AB}=\sqrt{(x_{1}-x_{2})^2+(y_{1}-y_{2})^2}

余弦定理:已知三个边长:

\cos A=\frac{b^2+c^2-a^2}{2bc}

由以上两个公式,即可求出长度与角度。

既然算法这么简单,软件还不是一大堆,那找度娘应该也是有的吧,的确,是有的,而且功能远比这两个多,像是数据记录之类的,但是软件吧,需要安装,安装后我发现大部分都没有比例计算的功能。

安装劝退,那在线的总应该有吧,实际上我没找到,要是有谁知道的话,告诉我一下。

编程语言的选择

那我自己写总应该可以了吧,嗯,可是语言那么多,选啥呢?要求有以下三条

  1. 实现功能,测量长度值,测量角度;
  2. 不需要安装,不需要配置;
  3. 在哪里都能用;

emmm,有没有感觉,这就是在线的那种东西,那用浏览器就好了嘛,所以写个HTML就可以了,我用 Sublime Text 编辑的。而且我写的这个代码,肯定很烂,我是逮着啥能用就用啥(能跑起来就好),如果有啥建议的话,请不吝赐教。

可是我懒得去整网站,那就自己写写自己用吧(如果有大牛看到了,把这个功能做成工具网页,挂在服务器上,然后给我个网址,感激不尽)。

具体算法分析

首先,分析分析这个代码该怎么写

本着怎么简单怎么来的原则,我们要花最小的力气,得到这些结果,当然,关于记录,我们可以在电脑前放一个草稿纸,用笔标一下就好了,这年头,纸和笔还是不能被淘汰掉的,要练字啊,写一手好字,自信心也就上来了。

步骤

分析一下操作上不可或缺的操作步骤:

  • 打开图片;
  • 用鼠标选取要测的点;
  • 输入参照物的尺寸。

嗯,有着三种操作就可以了,其他的我们都可以在点选完之后,直接给出结果。

界面

于是,界面上应该有:

  • 打开图片的浏览窗格;
  • 写参照物大小的输入框;
  • 显示一个图片。

嗯,有着三个要素也就足够了。结果可以直接写在图片上。

操作步骤

步骤与界面相关联,获取操作步骤:

  1. 打开照片;
  2. 如果我没有参照物的尺寸,或者干脆没有参照物,那我点图片就直接得到点的坐标,选三个点,以第二点为A点,计算该点的角度,结果显示在图片左上角;
  3. 如果输入了参照物尺寸,那么,默认选择的前两个点是参照物尺寸点,所以,给出像素坐标与尺寸的比例;
  4. 之后就全是测量距离,直到参照物尺寸栏的数字被删除。

其他要求

  • 按下鼠标后,应当在所选点位生成一个标记
  • 松开鼠标拖动,应当绘制一条当前位置到上一标记点的连线
  • 再按下鼠标后,得到长度的计算

根据上面的步骤开始码代码

HTML框架

最简单的HTML,就是打开之后是空白的,上面有个标题,就这样

<!doctype html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>长度与角度的测量</title>
        <style>

        </style>
    </head>
    <body>
        <script type="text/javascript ">

        </script>
    </body>
</html>

然后往里面填东西就行。

CSS样式

在<style> </style>里面填上css的东西,设置一下网页文本的居中,定义一块画布

p {
    text-align:center;
}
/*  #XX,叫做“id类型的选择器”   */
/*  .XX,叫做“class类型的选择器”*/
/*  XX,叫做“标签类型的选择器”  */
#oc { 
    background-color:#595959;
    display:block;
    margin:0 auto;
}

在<body> </body>里,写上下面的代码。把刚才要的那三个必须元素显示出来:

<input id= "file1" type="file" οnchange= "selectImage(this);"/> 
<p id = "ca">参照物长度: <input id="ref" type="text" />mm</p>
<canvas id="oc" width="400" height="400" ><span>不支持canvas的浏览器</span></canvas><br>

JavaScript的编写

接下来所有的东西就都是用JS写的了,都放在<script type="text/javascript "> </script>里:

定义变量

定义一些后续要用的变量,直接显示帮助文档

var circleNum = 0;
var scale = -1;
var fileURL = "";
var thePoint = [];
var yImg = new Image();
var isAngle = true;
var CalCompleted = false;  // 计算完成标志
var VerticalMode = false;
var reff = "";

var T$ = function(id){return document.getElementById(id);};//匿名函数
(function (){
	var canvas = T$("oc");
    var ctx = canvas.getContext('2d');
    ctx.font = "italic 35px 黑体";
    ctx.fillStyle = "red";
    ctx.fillText("1、要打开一个图片才行;",60,60,200);
    ctx.fillText("2、输入标定块大小就是测长度;",60,120,240);
    ctx.fillText("3、不输入就是测第二个点的角度。",60,180,280);
    ctx.fillText("4、shift,绘制直线",60,240,180);
}()); //匿名自执行函数,即定义后即执行,俗称“匿名包裹器”,只是为了保护canvas这几个变量与外面的变量不会起冲突,用上没啥坏处

选择并显示图片

function selectImage(file) {
    if (!file.files || !file.files[0]) {
        return;
    }
    fileURL =window.URL.createObjectURL(file.files[0]) //转换为url对象
    displayImage(fileURL);
    //T$('file1').hidden="hidden" //隐藏标签
}

function displayImage(filePath){
    document.getElementById("ref").value="";
    circleNum = 0;
    scale = -1;
    thePoint = [];
    isAngle = true;
    CalCompleted = false;
    var Oc=T$("oc");
    var Gc=Oc.getContext("2d");
    yImg.src=filePath;	
    yImg.οnlοad=function(){
        Oc.width= yImg.width;
        Oc.height=yImg.height;
        Gc.drawImage(yImg,0,0);//插入图片,给某一个区域插入背景图片,并设置平铺方式
    };
}

获取坐标和画圆的功能函数

function getPointOnCanvas(canvas,x,y){
    var bbox = canvas.getBoundingClientRect();
    return{x: (x-bbox.left) *(canvas.width / bbox.width),//求出鼠标的真实位置,然后乘以画布比例尺
        y: (y-bbox.top) * (canvas.height / bbox.height)
    };
}

function drawCircle(ctx,x,y,r,color){
    ctx.beginPath();
    ctx.arc(x, y, r, 0, Math.PI*2);
    
    ctx.lineWidth=2;
    ctx.strokeStyle=color;//属性定义必须有,要不然重绘的时候,会以上一段线为蓝本
    ctx.moveTo(x-2*r,y);
    ctx.lineTo(x+2*r,y);
    ctx.moveTo(x,y-2*r);
    ctx.lineTo(x,y+2*r);
    ctx.stroke();//绘制

    ctx.closePath();//如果
}

Shift快捷键 

document.onkeydown = function(e){
   var e = e||event;
   var currKey = e.keyCode||e.which||e.charCode;
   if(currKey == 16){VerticalMode = true;}//shift 快捷键
};
document.onkeyup = function(e){
   var e = e||event;
   var currKey = e.keyCode||e.which||e.charCode;
   if(currKey == 16){VerticalMode = false;}
}

鼠标事件

鼠标点击事件:

此部分主要功能为1、更改当前模式;2、采集选定的点;3、计算长度与角度。

oc.onmousedown = function(e){//获取在画布上的鼠标点击事件
    if(fileURL==""){
        circleNum = 0;
        return;
    }//没有图片,直接返回

    if(T$("ref").value == ""){
        isAngle = true; // 设置当前模式为角度测量
        scale = -1;     // 清空比例
    }else{ //否则判断是否需要重新计算比例
        isAngle = false; // 设置当前模式为长度测量
        if (T$("ref").value != reff){ //如果数字变了,后面要重新计算的
            scale = -1;
            reff = T$("ref").value
        }  
    }

    var canvas = T$("oc");
    var ctx = canvas.getContext('2d');
    var mpos = getPointOnCanvas(canvas,e.x,e.y);
    var po = {
        X: mpos.x,
        Y: mpos.y
    };
    if(circleNum > 0 && VerticalMode){
        if(Math.abs(thePoint[circleNum-1].X - po.X)>Math.abs(thePoint[circleNum-1].Y - po.Y)){
            po.Y = thePoint[circleNum-1].Y;
        }else if(Math.abs(thePoint[circleNum-1].X - po.X)<Math.abs(thePoint[circleNum-1].Y - po.Y)){
            po.X = thePoint[circleNum-1].X;
        }
    }
    thePoint[circleNum] = po;
    drawCircle(ctx,mpos.x,mpos.y,10,"#272822");
    circleNum = circleNum + 1;
    
    ctx.font = "italic 35px 黑体";
    ctx.fillStyle = "red";
    ctx.fillText(circleNum,mpos.x,mpos.y,200);  

    if(isAngle){  //计算角度
        if(circleNum == 3){ //采集到三个点才能计算角度
            var disa = ((thePoint[0].X-thePoint[1].X)**2+(thePoint[0].Y-thePoint[1].Y)**2)**(1/2);
            var disb = ((thePoint[2].X-thePoint[1].X)**2+(thePoint[2].Y-thePoint[1].Y)**2)**(1/2);
            var disc = ((thePoint[2].X-thePoint[0].X)**2+(thePoint[2].Y-thePoint[0].Y)**2)**(1/2);
            var angleNum = Math.acos((disa**2+disb**2-disc**2)/(2*disa*disb))*180/Math.PI; //必须得有合规的分号结尾
            ctx.beginPath();
            ctx.fillStyle="rgb(73,72,62)";
            ctx.rect(0,10,210,50);
            ctx.rect(thePoint[1].X,thePoint[1].Y,50,30); //右上角,宽,高
            ctx.fill();
            ctx.fillStyle="rgb(185,248,242)";
            ctx.fillText("角度“ 2 ”是:" + angleNum.toFixed(1)+"°",10,50,200); // 字符,左下角,长度
            ctx.fillText(angleNum.toFixed(1)+"°",thePoint[1].X,thePoint[1].Y+30,50); 
            CalCompleted = true;
        }else if(circleNum > 3){
            ctx.drawImage(yImg,0,0);
            circleNum = 0;
            CalCompleted = false;
        }
    }else{   //计算距离
        if(circleNum == 2){ //采集到两个点就能计算长度
            if(scale == -1){
                var dis = ((thePoint[0].X-thePoint[1].X)**2+(thePoint[0].Y-thePoint[1].Y)**2)**(1/2);
                scale = reff/dis;
                ctx.fillText("计算了比例:"+scale,10,50,200);
                CalCompleted = true;
                //T$('ca').hidden="hidden" //隐藏标签
            }else{
                var disRes = ((thePoint[0].X-thePoint[1].X)**2+(thePoint[0].Y-thePoint[1].Y)**2)**(1/2)*scale;
                var xx = (thePoint[0].X+thePoint[1].X)/2;
                var yy = (thePoint[0].Y+thePoint[1].Y)/2;
                ctx.beginPath();
                ctx.fillStyle="rgb(73,72,62)";
                ctx.rect(0,10,210,50);
                ctx.rect(xx-25,yy-15,50,30);
                ctx.fill();
                ctx.fillStyle="rgb(185,248,242)";
                ctx.fillText("距离是:"+disRes.toFixed(3)+"mm",10,50,200); 
                ctx.fillText(disRes.toFixed(3),xx-25,yy+10,50);
                CalCompleted = true;
            } 
        }else if(circleNum > 2){
            ctx.drawImage(yImg,0,0);
            circleNum = 0;
            CalCompleted = false;
        }
    }
}

 鼠标移动事件:

此部分主要负责绘制给人看的东西

oc.onmousemove = function(e){
    if (CalCompleted || circleNum == 0 || fileURL==""){return;} //屏蔽响应
    var canvas = T$("oc");
    var ctx = canvas.getContext('2d');
    var mpos = getPointOnCanvas(canvas,e.x,e.y);
    //ctx.clearRect(0, 0, canvas.width, canvas.height); 
    ctx.drawImage(yImg,0,0); 
    ctx.beginPath();           //要改变线型,必须重新开始一个轨迹
    ctx.lineWidth=2;
    ctx.strokeStyle="rgb(2,100,30)";
    ctx.moveTo(thePoint[0].X,thePoint[0].Y);
    for (var i = 1; i <= circleNum; i++){
        if (i == circleNum){
            if(VerticalMode){
                if(Math.abs(thePoint[circleNum-1].X - mpos.x)>Math.abs(thePoint[circleNum-1].Y - mpos.y)){
                    ctx.lineTo(mpos.x,thePoint[circleNum-1].Y) 
                }else if(Math.abs(thePoint[circleNum-1].X - mpos.x)<Math.abs(thePoint[circleNum-1].Y - mpos.y)){
                    ctx.lineTo(thePoint[circleNum-1].X,mpos.y) 
                }
            }else{
                ctx.lineTo(mpos.x,mpos.y)  //连到鼠标现在的位置
            }
        }else{
            ctx.lineTo(thePoint[i].X,thePoint[i].Y);  //连到下一个点
        }
    } 
    ctx.stroke();              //绘制,连线
     
    for(var i = 1;i<=circleNum;i++){
        drawCircle(ctx,thePoint[i-1].X,thePoint[i-1].Y,10,"#272822");
        ctx.font = "italic 35px 黑体";
        ctx.fillStyle = "red";
        ctx.fillText(i,thePoint[i-1].X,thePoint[i-1].Y,200);
    }// 写数字 
    ctx.lineWidth=1;
    ctx.strokeStyle="#595959";
    ctx.moveTo(mpos.x,0);ctx.lineTo(mpos.x,canvas.height);
    ctx.moveTo(0,mpos.y);ctx.lineTo(canvas.width,mpos.y);
    ctx.stroke();//绘制十字线
}

结语

  • 把上面的代码在txt文本文档里按顺序组装起来,保存,改后缀为.html
  • 用浏览器打开这个文件,然后就可以开心的测量角度和长度了

还是一贯的态度,非主业人士,需要用最简单的办法,解决想解决的问题,当然,还是要高标准,严要求的,所以抱着要好好学习的心态,经常要反过头来修改代码,增加功能,每当想要增加功能的时候,就能意识到写代码需要模块化的重要性,如果乱七八糟的一堆代码,相加任何一个小功能都要大改,与其如此,不如一开始就规划好,每一步写什么功能,完成后不再修改,直接增加新功能,如此这般才能让自己的代码健康长寿。

发布了6 篇原创文章 · 获赞 6 · 访问量 3950

猜你喜欢

转载自blog.csdn.net/qq_35758003/article/details/91994115