在JS中定义和使用Vector2

news2024/11/27 6:15:28

概述

Vector2是GDSCript中表示二维向量的类型,你会发现无论在任何编程语言中,只要你想很好的实现2D绘图以及几何和物理相关,Vector2是你必须要实现的一个类。我之前学C++时就写过一个C++的版本。

本篇就介绍我自己在JavaScript中定义的Vector2类型。它将是我接下来基于Canvas和SVG进行绘图的类的基础类型。

定义Vector2类型

在这里插入图片描述

// =======================================================
// Vector2
// 自定义类,Javascript版本的2D向量类型
// 巽星石
// 2024年10月6日13:18:59
// 2024年10月6日17:19:54
// 2D向量类型
// =======================================================
class Vector2{
    /*==================== 构造函数 ====================*/ 
    constructor(x,y){ // 构造函数
        this.x = x
        this.y = y
    }
    /*==================== 静态方法 ====================*/
    static ZERO(){
        return new Vector2(0,0);
    }
    static LEFT(){
        return new Vector2(-1,0);
    }
    static RIGHT(){
        return new Vector2(1,0);
    }
    static UP(){
        return new Vector2(0,-1);
    }
    static DOWN(){
        return new Vector2(0,1);
    }
    static ONE(){
        return new Vector2(1,1);
    }
    static ROTATED(deg){ //返回与X轴夹角为deg度的单位向量
        var vec = this.RIGHT();
        vec.rotated(deg);
        return vec;
    }
    static pVector2(ang,len){ // 返回极坐标点
        if(typeof(ang) == "number" && typeof(len) == "number"){
            return this.ROTATED(ang).times(len);
        }
    }
    static rad_to_deg(rad){ // 弧度转角度
        return rad * (180.0 / Math.PI) ;
    }

    static deg_to_rad(deg){ // 角度转弧度
        return deg * (Math.PI / 180.0) ;
    }

    static sin(deg){  //修正后的正弦函数
        const PI2 = Math.PI * 2    //2π
        let ang_rad = Vector2.deg_to_rad(deg);  //转为弧度
        let yu = Math.abs(ang_rad % PI2)        
        //所有正负180度或360度 + 任意圈数的sin值设为0
        return (yu != Math.PI && yu != 0) ? Math.sin(ang_rad) : 0
    }

    static cos(deg){  //修正后的余弦函数
        const PI2 = Math.PI * 2    //2π
        let ang_rad = Vector2.deg_to_rad(deg);  //转为弧度
        let yu = Math.abs(ang_rad % PI2)        
        //所有正负90度 + 任意圈数的cos值设为0
        return (yu != Math.PI/2) ? Math.cos(ang_rad) : 0
    }
    /*==================== 方法 ====================*/ 
    distance_to(b){  // 返回到b点的距离
        if(b.constructor.name == "Vector2"){
            const dx = this.x - b.x;
            const dy = this.y - b.y;
            return Math.sqrt(Math.pow(dx,2) + Math.pow(dy,2))
        }
    }
    
    direction_to(b){  // 返回到b点的方向
        if(b.constructor.name == "Vector2"){
            let vec = b.minus(this);  // AB向量 = B - A
            return vec.normalized();
        }
    }

    length(){ //返回向量的长度
        const o = new Vector2(0,0); //坐标系原点
        return this.distance_to(o)
    }

    normalized(){ // 获取归一化向量
        return this.divide(this.length());
    }

    /*==================== 角度相关 ====================*/ 
    angle(){ // 与X轴夹角(角度)
        return Vector2.rad_to_deg(Math.asin(this.normalized().y));
    }

    angle_to(b){ // 到b向量的夹角(角度)
        if(b.constructor.name == "Vector2"){
            return b.angle() - this.angle();
        }
    }
    rotated(deg){ // 旋转向量
        const r  = this.length();
        const ang = this.angle();
        
        var ang_rad = deg + ang;

        this.x = r * Vector2.cos(ang_rad);
        this.y = r * Vector2.sin(ang_rad);
    }
    /*==================== 运算 ====================*/
    plus(b){ //加法
        switch (typeof(b)) {
            case "object":
                if(b.constructor.name == "Vector2"){
                    return new Vector2(this.x + b.x,this.y + b.y)
                }
                break;
        
            default:
                break;
        }
    }

    minus(b){ //减法
        switch (typeof(b)) {
            case "object":
                if(b.constructor.name == "Vector2"){
                    return new Vector2(this.x - b.x,this.y - b.y)
                }
                break;
        
            default:
                break;
        }
    }
    
    times(b){ //乘法
        switch (typeof(b)) {
            case "number":
                return new Vector2(this.x * b,this.y * b)
                break;
            case "object":
                if(b.constructor.name == "Vector2"){
                    return new Vector2(this.x * b.x,this.y * b.y)
                }
                break;
            default:
                break;
        }
    }

    divide(b){ //除法
        switch (typeof(b)) {
            case "number":
                return new Vector2(this.x/b,this.y/b)
                break;
            case "object":
                if(b.constructor.name == "Vector2"){
                    return new Vector2(this.x / b.x,this.y / b.y)
                }
                break;
            default:
                break;
        }
    }
}

在网页中使用Vector2类型

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="1.js"></script>
</head>
<body>
    
</body>
<script>
    var a = new Vector2(100,100);
    console.log(a);
</script>
</html>

运行效果:

向量的长度

向量的长度是指到坐标系原点(0,0)的距离。

var a = new Vector2(100,100);
var b = new Vector2(200,100);
console.log(a.length());
console.log(b.length());

两点间距离

var a = new Vector2(100,100);
var b = new Vector2(200,100);
console.log(a.distance_to(b));

到点B的方向向量

var a = new Vector2(100,100);
var b = new Vector2(200,100);
console.log(a.direction_to(b));

归一化向量

var a = new Vector2(100,100);
var b = new Vector2(200,100);
console.log(a.normalized());

四则运算

加法

var a = new Vector2(100,100);
var b = new Vector2(200,100);
console.log(a.plus(b));

减法

var a = new Vector2(100,100);
var b = new Vector2(200,100);
console.log(a.minus(b));

乘法

var a = new Vector2(100,100);
var b = new Vector2(200,100);
console.log(a.times(b));   # 乘以一个Vector2
console.log(a.times(2));   # 乘以一个标量

除法

var a = new Vector2(100,100);
var b = new Vector2(200,100);
console.log(a.divide(b));   # 除以一个Vector2
console.log(a.divide(2));   # 除以一个标量

求角度

我们知道$ \sin \theta = \frac{y}{r} ,其中 r = 1 ,则 ,其中r = 1,则 ,其中r=1,则 \sin \theta = y ,则 ,则 ,则 \theta = \arcsin (y) $。

也就是我们要求一个向量与X轴的夹角θ,只需要求其归一化向量的y坐标的反正弦函数即可。

angle(){ // 与X轴夹角(角度)
  return this.rad_to_deg(Math.asin(this.normalized().y));
}

与X轴正方向的夹角

var a = new Vector2(100,100);
var b = new Vector2(200,100);
console.log(a.angle());
console.log(b.angle());

到B向量的夹角

var a = new Vector2(100,100);
var b = new Vector2(200,100);
console.log(a.angle_to(b));
console.log(b.angle_to(a));

向量旋转

var a = new Vector2(100,100);
console.log(a.length());
a.rotated(45)
console.log(a);
console.log(a.length());

常用单位向量和极坐标点

仿照GDSCript中的Vector2,设计了一些静态方法用于获取一些常用的单位向量。

static ZERO(){
    return new Vector2(0,0);
}
static LEFT(){
    return new Vector2(-1,0);
}
static RIGHT(){
    return new Vector2(1,0);
}
static UP(){
    return new Vector2(0,-1);
}
static DOWN(){
    return new Vector2(0,1);
}
static ONE(){
    return new Vector2(1,1);
}
static ROTATED(deg){ //返回与X轴夹角为deg度的单位向量
    var vec = this.RIGHT();
    vec.rotated(deg);
    return vec;
}
static pVector2(ang,len){ // 返回极坐标点
    if(typeof(ang) == "number" && typeof(len) == "number"){
        return this.ROTATED(ang).times(len);
    }
}

ROTATED()是我自己原创的,可以快速的获取与X轴夹角为deg度的单位向量。

pVector2()返回与X轴夹角为deg度,长度为len的向量。

var a = Vector2.pVector2(-90,100)
console.log(a)

角度与弧度互转

static rad_to_deg(rad){ // 弧度转角度
    return rad * (180.0 / Math.PI) ;
}

static deg_to_rad(deg){ // 角度转弧度
    return deg * (Math.PI / 180.0) ;
}

修正后的正弦和余弦函数

Math.sin(Math.PI * n)Math.sin(Math.PI * 2 * n)以及Math.cos((Math.PI / 2) * n)返回的都不是0,而是一个极小数。这对极坐标和向量旋转来说是没法用的,所以自己在Vector2类型中定义了cos()sin(),并做了一些修正。

static sin(deg){  //修正后的正弦函数
    const PI2 = Math.PI * 2    //2π
    let ang_rad = Vector2.deg_to_rad(deg);  //转为弧度
    let yu = Math.abs(ang_rad % PI2)        
    //所有正负180度或360度 + 任意圈数的sin值设为0
    return (yu != Math.PI && yu != 0) ? Math.sin(ang_rad) : 0
}

static cos(deg){  //修正后的余弦函数
    const PI2 = Math.PI * 2    //2π
    let ang_rad = Vector2.deg_to_rad(deg);  //转为弧度
    let yu = Math.abs(ang_rad % PI2)        
    //所有正负90度 + 任意圈数的cos值设为0
    return (yu != Math.PI/2) ? Math.cos(ang_rad) : 0
}

在HTML5 Canvas中使用

在HTML中定义canvas标签:

<body>
    <canvas id="canvas01" width="300" height="300"></canvas>
</body>

使用Vector2定义点并进行绘制:

// 获取canvas和上下文对象
let canvas = document.getElementById("canvas01");
let ctx = canvas.getContext('2d');

// 定义两点
let p1 = Vector2.ZERO()
let p2 = Vector2.pVector2(45,100)

// 绘制
ctx.beginPath()
ctx.strokeStyle = "red"; //轮廓颜色
ctx.lineWidth = 2;       //轮廓线宽
// 绘制直线
ctx.moveTo(p1.x,p1.y);
ctx.lineTo(p2.x,p2.y);
ctx.stroke();

为了方便查看画布,定义如下CSS样式:

body{
    background-color: #444;
}
canvas{
    background-color: #fff;
}

绘制效果:

总结

本文基于JavaScript编写了Vector2类型,基本实现与GDSCript内置Vector2类型相同的效果。

点积、叉积等内容后续将逐步实现和添加。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2192779.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于ssm 框架的java 开发语言的 在线教育学习平台系统设计与实现 源码 论文

博主介绍&#xff1a;专注于Java&#xff08;springboot ssm springcloud等开发框架&#xff09; vue .net php phython node.js uniapp小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆…

AI周报(9.29-10.5)

AI应用-Elayne公司临终规划和自动化遗产结算 创业公司Elayne成立于2023年&#xff0c;由Adria Ferrier和Jake Grafenstein共同创立&#xff0c;Adria Ferrier担任CEO&#xff0c;总部位于科罗拉多州丹佛市。 Elayne公司专注于遗产规划和结算领域&#xff0c;通过人工智能技术…

实验4 循环结构

1、判断素数 【问题描述】从键盘输入一个大于1的正整数&#xff0c;判断是否为素数 【输入形式】输入一个正整数 【输出形式】输出该数是否为素数 【样例输入】10 【样例输出】10 is not a prime number 【样例说明】样例2 输入&#xff1a;-10 输出&#xff1a;error! #de…

实景三维赋能矿山安全风险监测预警

随着科技的不断进步&#xff0c;实景三维技术在矿山安全风险监测预警中的应用越来越广泛&#xff0c;它为矿山安全管理带来了革命性的变革。 一、矿山安全现状 矿山作为国家重要的能源和原材料基地&#xff0c;其安全生产直接关系到国民经济的发展和社会的稳定。然而&#xf…

【前端vue2 + element ui】Dialog 对话框:.vue组件跳转

【前端vue2 element ui】Dialog 对话框&#xff1a;.vue组件跳转 写在最前面一、父组件调用1、<template>1.1 跳转位置1.2 弹窗调用 2、<script>2.1 import2.2 export2.3 methods 二、子组件调用1、<template>2、<script>2.1 export2.2 watch和method…

不可错过!CMU最新《生成式人工智能大模型》课程:从文本、图像到多模态大模型

1. 课程简介 从生成图像和文本到生成音乐和艺术&#xff0c;生成模型一直是人工智能的关键挑战之一。本课程将探讨推动生成模型和基础模型&#xff08;Foundation Models&#xff09;最近进展的机器学习和人工智能技术。学生将学习、开发并应用最先进的算法&#xff0c;使机器…

windows下,在vscode中使用cuda进行c++编程

安装cuda CUDA Toolkit Downloads | NVIDIA Developer 这里网上教程多的是&#xff0c;在这个网址下载安装即可 我这台电脑因为重装过&#xff0c;所以省去了安装步骤&#xff0c;但是要重新配置环境变量。我重新找到了重装之前的CUDA位置(关注这个bin文件夹所在的目录) 在…

Canvas指纹:它是什么以及如何避免被Canvas指纹识别跟踪

Canvas指纹识别技术已成为追踪在线行为的一种隐蔽手段。尽管这个技术在某些方面有其正当用途&#xff0c;它也可能被用于监视我们的在线活动&#xff0c;不经我们的同意就收集个人信息。 你认为启用Canvas指纹禁用功能就能使你在网络上无迹可寻吗&#xff1f;可能需要重新考虑…

开放式耳机是什么意思?漏音吗?开放式的运动蓝牙耳机推荐

目前运动耳机市场主要分为入耳式、骨传导和开放式三类。入耳式耳机占比30%-40%&#xff0c;虽目前占比较大&#xff0c;但因在运动场景下有闷塞感、出汗不适、屏蔽外界环境音带来安全隐患等缺点&#xff0c;占比会逐渐下降。 骨传导耳机占比也为30%-40%&#xff0c;其不堵塞耳…

macos 中使用macport安装,配置,切换多版本php,使用port 安装php扩展方法总结

macport是一款mac系统中比较优秀的软件包管理工具&#xff0c;他与brew的最大区别在于软件包的安装速度相当的快&#xff0c;以安装php为例&#xff0c; 使用port安装和使用brew安装&#xff0c;port方式安装要比brew方式安装要快最少10倍以上&#xff0c; 因为port安装软件包时…

javaScript数组(16个案例+代码+效果图)

目录 1.数组的概念 2.创建数组 1.通过数组字面量创建数组 1.代码 2.效果 2.通过new Array()创建数组 1.代码 2.效果 3.数组的基本操作 1.获取数组的长度 案例:获取数组的长度 1.代码 2.效果 2.修改数组的长度 1.代码 2.效果 4.访问数组 案例:访问数组 1.代码 2.效果 5.遍历数组…

实验3 选择结构

1、计算分段函数的值 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <math.h> int main() {double x,y0;scanf("%lf",&x);if(x<0){printf("error!\n");return 0;}if(0<x&&x<1){ylog10(x);}else if(1<…

第十四周:机器学习

目录 摘要 Abstract 一、生成式对抗网络&#xff08;下&#xff09; 1、回顾 2、生成式模型 3、generative评价指标 4、conditional generation 5、cycle GAN 二、总结 摘要 接着上周对GAN的初步概念了解及其理论推导&#xff0c;本周回顾了GAN难以训练的问题&#…

常见排序详解(历时四天,哭了,必须释放一下)

目录 1、插入排序 1.1 基本思想 1.2 直接插入排序 1.2.1 思路 1.2.2 代码实现 1.2.3 性质 1.3 希尔排序 1.3.1 思路 1.3.2 代码实践 1.3.3 性质 2、选择排序 2.1 基本思想 2.2 直接选择排序 2.2.1 思路 2.2.2 代码实践 2.2.3 性质 2.3 堆排序 2.3.1 思路 2.…

No.5 笔记 | 网络端口协议概览:互联网通信的关键节点

1. 常用端口速览表 端口范围主要用途1-1023系统或特权端口1024-49151注册端口49152-65535动态或私有端口 远程访问类&#xff08;20-23&#xff09; 端口服务记忆技巧安全风险21FTP"File Transfer Port"爆破、嗅探、溢出、后门22SSH"Secure Shell"爆破、…

基于 STM32F407 的 SPI Flash下载算法

目录 一、概述二、自制 FLM 文件1、修改使用的芯片2、修改输出算法的名称3、其它设置4、修改配置文件 FlashDev.c5、文件 FlashPrg.c 的实现 三、验证算法 一、概述 本文将介绍如何使用 MDK 创建 STM32F407 的 SPI Flash 下载算法。 其中&#xff0c;SPI Flash 芯片使用的是 W…

人工智能专业就业方向与前景

随着产业结构升级的持续推进&#xff0c;未来行业领域对于人工智能专业人才的需求量会逐渐增加&#xff0c;一部分高校也开始陆续在本科阶段开设人工智能专业&#xff0c;以缓解人工智能领域人才缺口较大的问题。下面是小编整理的人工智能专业就业方向与前景&#xff0c;欢迎阅…

Leecode热题100-41.缺失的第一个正数

给你一个未排序的整数数组 nums &#xff0c;请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,0] 输出&#xff1a;3 解释&#xff1a;范围 [1,2] 中的数字都在数组…

C++面向对象:继承!

前言 继承是面向对象三大特性之一&#xff0c;所有的面向对象的语言都具备这三个性质&#xff0c;我们之前已经介绍过了封装的相关概念&#xff0c;今天我们来学习一下第二大特性&#xff1a;继承。 一.继承的概念 什么是继承&#xff1f; 定义&#xff1a;继承&#xff08;…

AI相关的整理

AI相关的整理 初体验记不住如何部署如何微调 整理AI学习&#xff0c;AI小白&#xff0c;业余爱好。持续更新&#xff0c;谨慎参考&#xff01; 初体验 试了一下本地直接下载安装ollama和open-webui&#xff0c;然后运行指定的模型&#xff0c;跟着文档做&#xff0c;很简单。但…