图形编辑器:场景坐标、视口坐标以及它们之间的转换

news2024/9/28 16:27:05

大家好,我是前端西瓜哥。

图形编辑器的坐标系有两种。

一个是场景(scene)坐标系,一个是 视口(viewport)坐标系。视口就是场景的一个子区域。

假设我们的视口的原点,离场景原点的坐标水平和垂直距离分别为 scrollX 和 scrollY。

不考虑缩放,假设我们在视口坐标上的某个地方点击了一下,这个坐标是 (x, y)。这个坐标在场景坐标系中,就是:

const sceneX = scrollX + x;
const sceneY = scrollY + y;

挺简单。

视口坐标转换为场景坐标

下面我们引入画布缩放,即画布可以缩小和放大,对应的一个比例值 zoom。

视口中的某个坐标 (x, y) 在场景坐标系,则是 :

function viewportCoordsToSceneCoords(x, y, scrollX, scrollY, zoom) {
  return {
		x: scrollX + x / zoom,
		y: scrollY + y / zoom
  }
}

之所以要用 x 除以 zoom,是因为此时视口中展示的是缩放后的图形,里面的坐标都是缩放后的值。所以需要先转换为 zoom 值为 1 对应的真实值。

场景坐标转换为视口坐标

然后我们反过来,如何从场景坐标 (x, y) 转换为视口坐标?将前面的公式做等式变换即可:

function sceneCoordsToViewportCoords(x, y, scrollX, scrollY, zoom) {
  return {
		x: (x - scrollX) * zoom,
		y: (y - scrollY) * zoom
  };
}

我们通常是使用按键加滚轮的方式让画布以光标为中心进行缩放,或按按钮进行缩放,

为了让缩放后的场景还能对上缩放前光标的位置,我们需要计算缩放后的 scrollX 和 scrollY,进行校准。

核心思路是 保持缩放前点到视口左上角距离(视口坐标系)相同

function calScrollVal(cx, cy, prevZoom, zoom, scrollX, scrollY) {
  // 先计算目标点的场景坐标(这里 cx 和 cy 是基于视口坐标系的)
  const { x: sceneX, y: sceneY } = viewportCoordsToSceneCoords(cx, cy, prevZoom, scrollX, scrollY);
  // 缩放后画布缩放比变成了 zoom,距离视口左上角的距离变成了 cx / zoom
  // 减去这个距离,就是新的 scrollX 了。
  const newScrollX = sceneX - cx / zoom;
  const newScrollY = sceneY - cy / zoom;

  return {
    x: newScrollX,
    y: newScrollY
  };
}

再说点别的。

可能会有这么一种情况,就是实际的视口区域的原点坐标有一些偏移,偏移了 offsetX 和 offsetY,见下图。

我们只需要将前面代码中的 scrollX 变成 (scrollX + offsetX),scrollY 变成 (scrollY + offsetY),其他不变。

就这些了。

总结一下,视口坐标是场景坐标平移并缩放后的结果,所以视口转场景,需要除以 zoom 再加上偏移值。在图形编辑器中,会有相当多的坐标系转换逻辑,这两个坐标系的关系需要好好消化理解。

https://github.com/F-star/suika

我是前端西瓜哥,欢迎关注我,学习更多前端知识。

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

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

相关文章

C2芯片一ESP32-C2开发板

C2是一个芯片采用4毫米x 4毫米封装,与272 kB内存。它运行框架,例如ESP-Jumpstart和ESP造雨者,同时它也运行ESP-IDF。ESP-IDF是Espressif面向嵌入式物联网设备的开源实时操作系统,受到了全球用户的信赖。它由支持Espressif以及所有…

月报总结|Moonbeam 12月份大事一览

本月,针对生态和项目,Moonbeam基金会启动首期Accelerator Program孵化计划,将针对入选团队提供一系列扶持资源,申请仍在开放中。对于开发者,Moonbuilders Academy上线了关于构建跨链应用的新课程。 社区活动方面&…

【案例分析】汽车制造行业电能质量治理方案分析

摘要:现如今的汽车制造行业,使用的机械设备越来越精密,制造技术与自动化水平也越来越高,为此对供电系统的电能质量要求更高更苛刻,同时对不能满足现有生产工艺需求的供电质量进行治理。通过分析汽车制造过程中冲压工艺…

连续三年!Fortinet再次位列《 Gartner 企业级有线和无线局域网基础设施魔力象限报告》“远见者”

网络安全领导者Fortinet(NASDAQ:FTNT),近日宣布连续第三年入围《Gartner企业级有线和无线局域网基础设施魔力象限报告》“远见者”象限。这一殊荣源于Fortinet 旗下FortiSwitch 和 FortiAP等有线和无线局域网产品组合的强劲实力&a…

激光焊接薄板时需要注意的一些问题

焊接是目前工业制造中必要的工艺技术,焊接金属时需要注意很多方面的问题,本文主要针对金属薄板焊接中的一些工艺问题进行展开讨论。例如:焊缝的牢固程度、焊缝的平整度、焊缝直线度的控制、焊缝高度和宽度的控制以及焊接的均匀性等方面。 一、…

程序员们有什么好的编程习惯?

优良的代码显然不是制作优秀软件的唯一要素,但是主要的要素之一。我们可能拥有世界上最好的产品和营销团队,部署了最好的平台,并以最好的框架来构建软件,但归根结底,一款软件所做的一切,都是因为有人写了一…

LeetCode 49 字母异位词分组 | 解题思路分享

原题链接:49. 字母异位词分组 - 力扣(LeetCode) 题目难度:中等 题目描述 给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。 字母异位词 是由重新排列源单词的字母得到的一个新单词…

分析快、易操作的数据分析工具推荐

数据分析工具发展这么多年,该有的技术功能都有了,该提高的数据分析效率、数据分析量等也都提高了,但很多长期奋战在一线的数据分析人员却总是抱怨数据分析工具响应慢、分析慢、越来越容易崩溃。为什么要找一款分析快、易操作的数据分析工具还…

Git入门学习笔记(10分钟速通)

一、Git概述 官网翻译: Git 是一个免费的开源 分布式版本控制系统,旨在快速高效地处理从小型项目到大型项目的所有内容。 Git易于学习, 体积小,性能快如闪电。它超越了 Subversion、CVS、Perforce 和 ClearCase 等 SCM 工具&am…

13_2、Java的IO流概述

一、Java IO原理1、I/O是Input/Output的缩写, I/O技术是非常实用的技术,用于处理设备之间的数据传输。如读/写文件,网络通讯等。2、Java程序中,对于数据的输入/输出操作以“流(stream)” 的方式进行。3、java.io包下提供了各种“流…

kaggle平台学习复习笔记 | XGBoost、LightGBM and Catboost

这里写目录1.XGBoost官方文档介绍与使用2.LightGBM官方文档介绍与使用3.CatBoost官方文档介绍与使用对比数据预处理如下,下文不再重复 import lightgbm as lgb import xgboost as xgb from catboost import CatBoostRegressorfrom sklearn.model_selection import …

C#入门级——泛型、泛型类、泛型接口、泛型方法和泛型委托

目录 一、泛型(generic) 二、为什么需要泛型类 类型膨胀 成员膨胀 使用object类 三、泛型的定义 定义泛型类 使用泛型类 泛型接口​​​​​​​ 两种泛型接口的实现方法 泛型方法 成员膨胀 使用泛型 泛型委托 Action委托——只能引用没有…

有效的需求管理,需遵循四大原则。

1、需求管理必须与需求工程活动相整合 需求管理必须与需求工程的其他活动紧密整合,进行需求管理一定不能脱离需求工程,需求工程包括了需求获取、需求分析、需求描述、需求验证、需求管理,因而需求管理必须与前面的几个需求阶段保持密切相关。…

2023/1/9 Vue学习笔记-5-TodoList案例

1 静态组件 App.vue <template><div class"todo-container"><div class"todo-wrap"><UserHeader/><UserList/><UserFooter/></div></div> </template> <script>import UserHeader from &qu…

【计组】FPGA和ASIC--《深入浅出计算机组成原理》(七)

课程链接&#xff1a;深入浅出计算机组成原理_组成原理_计算机基础-极客时间 目录 一、FPGA &#xff08;一&#xff09;FPGA 的解决方案步骤 1、用存储换功能实现组合逻辑 2、对于需要实现的时序逻辑电路&#xff0c;在 FPGA 里面直接放上 D 触发器&#xff0c;作为寄存…

工业清洗企业资质证书

工业清洗在美国、日本、新加坡、西欧等国发展较早&#xff0c;已经建立起专业化程度很高的化学清洗体系。我国的工业清洗发展很快&#xff0c;目前已经初步形成了新兴的清洗产业网络&#xff0c;清洗技术也已达到国际先进水平&#xff0c;具备了清洗大型设备的能力和经验。 工业…

CANoe-诊断控制台实现同一个目标ECU的物理寻址和功能寻址

接触过UDS诊断的人应该知道,诊断通信有两种方式:物理寻址和功能寻址。那什么是物理寻址和功能寻址呢? 简单点说,物理寻址是单播,功能寻址是多播。具体来说,由于UDS诊断通信的C/S模式(客户端Tester/服务器ECU),物理寻址是Tester发送的诊断请求,只有一个目标ECU回复诊…

MySQL模块

目录 1.在项目中操作数据库的步骤 2.安装与配置 mysql 模块 1.安装模块 2.配置mysql模块 3.测试模块是否正常工作 3.使用 mysql 模块操作 MySQL 数据库 查询数据&#xff1a; 插入数据&#xff1a; 快捷插入数据&#xff1a; 更新数据&#xff1a; 快捷更新数据&am…

node.js(3)--线程和进程、node简介

目录 进程和线程 Node.js 简介 历史 进程和线程 进程 负责为程序的运行提供必备的环境就相当于工厂中的车间&#xff08;专门存放代码的地方&#xff09; 线程 计算机中最小的计算单位&#xff0c;线程负责进程中的程序就相当于工厂中的工人 单线程 JS是单线程 多线程 …

ansible (第三天)

2.6 lineinfile模块 lineinfile模块&#xff0c;确保"某一行文本"存在于指定的文件中&#xff0c;或者确保从文件中删除指定的"文本"&#xff08;即确保指 定的文本不存在于文件中&#xff09;&#xff0c;还可以根据正则表达式&#xff0c;替换"某一…