函数图像绘制小工具

news2024/11/13 12:48:19

函数图像绘制小工具

    • 文章说明
    • 核心代码
    • 效果展示
    • 源码下载

文章说明

方便绘制一些数学的基础图象,制作该款小工具,不过尚为雏形阶段,等待后续逐步完善

核心代码

采用canvas绘图实现,核心代码如下

<script setup>
import {onMounted, reactive} from "vue";
import {ElMessage} from "element-plus";

const data = reactive({
  visible: false,
  xStart: "-10",
  xEnd: "10",
  expression: "y=x",
  point: {
    x: "",
    y: "",
  },
  tagPointList: [
    {
      id: 1,
      x: 0,
      y: 0,
    }
  ]
});

let canvas;
let context;
const xList = [];
const yList = [];
let width;
let height;

onMounted(() => {
  canvas = document.getElementsByClassName("canvas")[0];
  width = canvas.width;
  height = canvas.height;
  context = canvas.getContext("2d");
});

function clearCanvas() {
  context.clearRect(0, 0, width, height);
}

function draw() {
  if (!data.expression.startsWith("y=")) {
    ElMessage({
      message: "表达式应以y=开头",
      type: 'warning',
    });
    return;
  }

  xList.length = 0;
  for (let i = parseFloat(data.xStart); i <= parseFloat(data.xEnd); i += 0.1) {
    xList.push(parseFloat(i.toFixed(2)));
  }

  yList.length = 0;
  for (let i = 0; i < xList.length; i++) {
    const expression = data.expression.replace("y=", "").replaceAll("-x", "-1*x").replaceAll("x", "(" + xList[i] + ")");
    yList.push(parseFloat(eval(expression).toFixed(2)));
  }

  // 绘制基准线、X轴、Y轴
  context.beginPath();
  context.strokeStyle = 'black';
  context.moveTo(20, height / 2);
  context.lineTo(width - 20, height / 2);
  context.moveTo(width / 2, 20);
  context.lineTo(width / 2, height - 20);
  context.stroke();
  const originX = width / 2;
  const originY = height / 2;

  // 绘制刻度
  const xValueList = [];
  const yValueList = [];
  for (let i = -10; i <= 10; i++) {
    xValueList.push(i);
  }
  for (let i = -7; i <= 7; i++) {
    yValueList.push(i);
  }
  const defaultTagPointList = [];
  for (let i = 0; i < xValueList.length; i++) {
    for (let j = 0; j < yValueList.length; j++) {
      defaultTagPointList.push({
        x: xValueList[i],
        y: yValueList[j],
      });
    }
  }

  // 计算单位长度表示的x、y坐标的距离
  context.strokeStyle = 'red';
  const eachLengthX = width / xValueList.length - 1;
  const eachLengthY = height / yValueList.length - 1;
  drawXValue(xValueList, originX, originY, eachLengthX);
  drawYValue(yValueList, originX, originY, eachLengthY);
  context.stroke();

  // 绘制每个点及每条线
  context.strokeStyle = 'black';
  for (let i = 0; i < xList.length - 1; i++) {
    const pos = getPos(xList[i], yList[i], originX, originY, eachLengthX, eachLengthY);
    context.moveTo(pos[0], pos[1]);
    const nextPos = getPos(xList[i + 1], yList[i + 1], originX, originY, eachLengthX, eachLengthY);
    context.lineTo(nextPos[0], nextPos[1]);

    // 绘制默认点
    if (isDefaultTagPoint(defaultTagPointList, xList[i], yList[i])) {
      context.arc(pos[0], pos[1], 3, 0, 2 * Math.PI);
    }
    if (i + 1 === xList.length - 1) {
      if (isDefaultTagPoint(defaultTagPointList, xList[i + 1], yList[i + 1])) {
        context.arc(nextPos[0], nextPos[1], 3, 0, 2 * Math.PI);
      }
    }
  }

  context.stroke();
  context.closePath();
}

function isDefaultTagPoint(defaultTagPointList, x, y) {
  for (let i = 0; i < defaultTagPointList.length; i++) {
    if (defaultTagPointList[i].x === x && defaultTagPointList[i].y === y) {
      return true;
    }
  }
  return false;
}

function drawXValue(xValueList, originX, originY, eachLengthX) {
  for (let i = 0; i < xValueList.length; i++) {
    if (xValueList[i] > 0) {
      context.moveTo(xValueList[i] * eachLengthX + originX, originY);
      context.lineTo(xValueList[i] * eachLengthX + originX, originY - 5);
      context.fillText(xValueList[i], xValueList[i] * eachLengthX + originX - 3, originY - 10);
    } else if (xValueList[i] < 0) {
      context.moveTo(xValueList[i] * eachLengthX + originX, originY);
      context.lineTo(xValueList[i] * eachLengthX + originX, originY + 5);
      context.fillText(xValueList[i], xValueList[i] * eachLengthX + originX - 5, originY + 20);
    }
  }
}

function drawYValue(yValueList, originX, originY, eachLengthY) {
  for (let i = 0; i < yValueList.length; i++) {
    if (yValueList[i] > 0) {
      context.moveTo(originX, yValueList[i] * eachLengthY + originY);
      context.lineTo(originX + 5, yValueList[i] * eachLengthY + originY);
      context.fillText(-yValueList[i] + "", originX - 15, yValueList[i] * eachLengthY + originY + 3);
    } else if (yValueList[i] < 0) {
      context.moveTo(originX, yValueList[i] * eachLengthY + originY);
      context.lineTo(originX - 5, yValueList[i] * eachLengthY + originY);
      context.fillText(-yValueList[i] + "", originX + 5, yValueList[i] * eachLengthY + originY + 3);
    }
  }
}

function getPos(x, y, originX, originY, eachLengthX, eachLengthY) {
  const posX = parseFloat((x * eachLengthX + originX).toFixed(2));
  const posY = parseFloat((-y * eachLengthY + originY).toFixed(2));
  return [posX, posY];
}

function addPoint() {
  const x = parseFloat(data.point.x);
  const y = parseFloat(data.point.y);

  let flag = false;
  for (let i = 0; i < data.tagPointList.length; i++) {
    if (data.tagPointList[i].x === x && data.tagPointList[i].y === y) {
      flag = true;
      break;
    }
  }
  if (!flag) {
    ElMessage({
      message: "该点已被标注",
      type: 'warning',
    });
    return;
  }
  data.tagPointList.push({
    id: data.tagPointList.length + 1,
    x: x,
    y: y
  });
  draw();
}

function removeItem(row) {
  let flag = false;
  for (let i = 0; i < data.tagPointList.length; i++) {
    if (data.tagPointList[i].x === row.x && data.tagPointList[i].y === row.y) {
      flag = true;
      data.tagPointList.splice(i, 1);
      break;
    }
  }
  if (!flag) {
    ElMessage({
      message: "该点未被标注",
      type: 'warning',
    });
    return;
  }
  draw();
}
</script>

<template>
  <div class="container">
    <el-row style="justify-content: center">
      <el-input v-model="data.xStart" placeholder="x起始值" style="width: 110px"/>
      <el-input v-model="data.xEnd" placeholder="x结束值" style="width: 110px; margin-left: 20px"/>
      <el-input v-model="data.expression" placeholder="函数表达式" style="width: 240px; margin-left: 20px"/>
      <el-button style="margin-left: 20px" type="primary" @click="draw">绘图</el-button>
      <div style="position: absolute; right: 0">
        <el-button type="primary" @click="clearCanvas">清空绘图内容</el-button>
      </div>
    </el-row>
    <el-row style="justify-content: center; margin-top: 20px">
      <el-input placeholder="x" style="width: 240px"/>
      <el-input placeholder="y" style="width: 240px; margin-left: 20px"/>
      <el-button style="margin-left: 20px" type="primary" @click="addPoint">标点</el-button>
      <div style="position: absolute; right: 0">
        <el-button type="primary" @click="data.visible = true">查看标点信息</el-button>
      </div>
    </el-row>

    <canvas class="canvas" height="700" width="1000"></canvas>
  </div>

  <el-dialog v-model="data.visible" title="标点信息" width="80%">
    <el-table :data="data.tagPointList" border>
      <el-table-column align="center" label="编号" prop="id"/>
      <el-table-column align="center" label="X坐标" prop="x"/>
      <el-table-column align="center" label="Y坐标" prop="y"/>
      <el-table-column align="center" label="操作">
        <template #default="scope">
          <el-button type="danger" @click="removeItem(scope.row)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
  </el-dialog>
</template>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.container {
  margin: 50px auto;
  width: fit-content;
}

.canvas {
  margin-top: 50px;
  box-shadow: 0 0 20px 3px #88888888;
}
</style>

效果展示

y=x^3
在这里插入图片描述

y=x^2
在这里插入图片描述

y=1/x
在这里插入图片描述

y=2^x
在这里插入图片描述

源码下载

函数图像绘制小工具

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

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

相关文章

公开数据库汇总及下载(1)-TCGA

文章目录 1. 常用数据库2. TCGA数据下载2.1 TCGA介绍2.2 数据类型详解2.3 TCGA数据等级2.4 TCGA网页筛选页面介绍2.5 各文件每个字段的含义2.6 biospecimen IDs介绍(样本ID)2.7 下载代码 参考文件 本文的内容介绍、代码下载主要参考了网上多个文件汇总而成&#xff0c;本文仅作…

深度学习基础—Dropout正则化(随机失活正则化)

1.Dropout正则化 Dropout正则化&#xff1a;网络中每一个神经元节点都有一定概率保留或消除&#xff0c;从而下较小规模的网络。 假设上图网络存在过拟合&#xff0c;Dropout正则化的做法就是遍历每一个节点&#xff0c;对该节点以一定的概率保留或删除&#xff0c;从而简化网络…

C语言 | Leetcode C语言题解之第347题前K个高频元素

题目&#xff1a; 题解&#xff1a; struct hash_table {int key;int val;// 查看 https://troydhanson.github.io/uthash/ 了解更多UT_hash_handle hh; };typedef struct hash_table* hash_ptr;struct pair {int first;int second; };void swap(struct pair* a, struct pair*…

【C++】udp通信协议详解和示例

前言 UDP&#xff08;‌User Datagram Protocol&#xff0c;‌用户数据报协议&#xff09;‌是一种无连接的、‌不可靠的、‌面向数据报的传输层协议。‌它广泛应用于需要高实时性且对数据传输可靠性要求不高的场景&#xff0c;‌如实时音视频传输、‌在线游戏等。‌本文将详细…

Python做统计图之美

Python数据分析可视化 案例效果图 import pandas as pd import matplotlib.pyplot as plt import matplotlib# 数据 data {"房型": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],"住宅类型": ["普通宅", "普通宅", "普通宅", &q…

JUC6-共享模型之不可变

目录 日期转换的问题 问题提出 思路&#xff1a;同步锁 思路&#xff1a;不可变 不可变设计 final的使用 保护性拷贝 享元模式 概述 体现 包装类 String串池 BigDecimal BigInteger DIY 原理&#xff1a;final 无状态 日期转换的问题 问题提出 下面的代码在运…

python井字棋游戏设计与实现

python实现井字棋游戏 游戏规则&#xff0c;有三个井字棋盘&#xff0c;看谁连成的直线棋盘多谁就获胜 棋盘的展现形式为 棋盘号ABC和位置数字1-9 输入A1 代表在A棋盘1号位数下棋 效果图如下 部分源码如下&#xff1a; 卫星工纵浩 白龙码程序设计&#xff0c;点 代码获取 …

Stability AI发布了单目视频转4D模型的新AI模型:Stable Video 4D

开放生成式人工智能初创公司Stability AI在3月发布了Stable Video 3D&#xff0c;是一款可以根据图像中的物体生成出可旋转的3D模型视频工具。Stability AI在7月24日发布了新一代的Stable Video 4D&#xff0c;增添了赋予3D模移动作的功能。 Stable Video 4D能在约40秒内生成8…

DBAPI如何用SQL将多表关联查询出树状结构数据(嵌套JSON格式)

场景描述 假设数据库中有3张表如下&#xff1a; 客户信息表 订单表 订单详情表 一个客户有多个订单&#xff0c;一个订单包含多个产品信息&#xff0c;客户-订单-产品就构成了3级的树状结构&#xff0c;如何查询出如下树状结构数据呢&#xff1f; [{"customer_age"…

矩阵--旋转图像

给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 &#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&a…

嵌入式Day33---Linux软件编程---网络编程

目录 一、TCP包头 1.1.源端口 1.2.目的端口 1.3.序号 1.4.确认号 1.5.数据偏移 1.6.保留 1.7.指令信号 1.SYN 2.ACK 3.FIN 4.RST 5.PSH 6.URG 1.8.窗口 1.9.校验和 1.10.紧急指针 二、TCP的过程 2.1三次握手 2.2.传输数据 ​编辑 2.3.四次挥手 ​编辑 …

SpringBoot依赖之Spring Data Redis 一 List 类型

概念 Spring Data Redis (AccessDriver) 依赖名称: Spring Data Redis (AccessDriver)功能描述: Advanced and thread-safe Java Redis client for synchronous, asynchronous, and reactive usage. Supports Cluster, Sentinel, Pipelining, Auto-Reconnect, Codecs and muc…

[Qt][QSS][下]详细讲解

目录 1.样式属性0.前言1.盒模型(Box Model) 2.常用控件样式属性1.按钮2.复选框3.单选框4.输入框5.列表6.菜单栏7.注意 1.样式属性 0.前言 QSS中的样式属性⾮常多&#xff0c;不需要都记住&#xff0c;核⼼原则是⽤到了就去查 ⼤部分的属性和CSS是⾮常相似的 QSS中有些属性&am…

Linux CentOS手动安装Node.js(图文教程)

本章教程主要介绍如何在centos7上安装指定版本的Node.js 一、下载nodejs 前往Node.js官网&#xff0c;根据自己需要安装的版本进行下载。 官网下载地址&#xff1a;https://nodejs.org/zh-cn/download/prebuilt-binaries 本文&#xff0c;以v14.21.3版本介绍整个安装过程。 二、…

<STC32G12K128入门第十步>USB HID键盘

前言 最近公司的一款低功耗的遥控器涉及到使用USB HID的功能,就是需要将BLE蓝牙读取的IC卡的数据在通过USB接口上传到电脑的记事本上面。 一、USB HID是啥? USB HID类是USB设备的一个标准设备类,包括的设备非常多。HID类设备定义它属于人机交互操作的设备,用于控制计算机…

IDEA 导入 RocketMQ 源码

目录 前言一、RocketMQ 架构二、环境准备三、下载源码四、编译源码4.1 导入源码4.2 目录结构4.3 运行程序1. 启动 Namesrv2. 启动 Broker3. 启动 Producer4. 启动 Consumer 五、监控平台的搭建5.1 下载 console 源码5.2 IDEA 启动 前言 最近项目中有个功能需要在本地调试下 Ro…

SAM2论文核心速览

官方博客&#xff1a; https://ai.meta.com/blog/segment-anything-2/ 官方论文&#xff1a;​​​​​​https://ai.meta.com/research/publications/sam-2-segment-anything-in-images-and-videos/ 一、研究背景 研究问题&#xff1a;这篇文章要解决的问题是如何在图像和视频…

公式编辑器 -vue-formula-editor

前言 公式编辑旨在帮助用户使用可视化的前提&#xff0c;能便捷的使用平台&#xff0c;例如低代码平台使用广泛 vue-formula-editor vue-formula-editor是一款开源的Vue公式计算组件&#xff0c;可以帮助开发者快速集成公式编辑 在线体验 demo & 源码 安装 npm i vue-form…

CentOS 上安装 Java 17

要在 CentOS 上安装 Java 17&#xff0c;您可以使用多种方法。这里我将向您展示如何通过下载 Oracle 提供的 Java 开发工具包 (JDK) 或使用其他开源 JDK 版本&#xff08;如 Adoptium 或 OpenJDK&#xff09;来完成安装。 方法一&#xff1a;使用 Oracle JDK 17 下载 JDK 17&a…

HTB-BoardLight靶机笔记

BoardLight靶机笔记 概述 HTB的靶机BoardLight 靶机地址&#xff1a;https://app.hackthebox.com/machines/BoardLight 一、nmap扫描 1&#xff09;端口扫描 -sT tcp全连接扫描 --min-rate 以最低速率10000扫描 -p- 扫描全端口 sudo nmap -sT --min-rate 10000 -p- -o p…