bresenham algorithm

news2024/11/16 10:38:28

#! https://zhuanlan.zhihu.com/p/598780689

bresenham algorithm

全象限区域bresenham algorithm计算的python/c++实现

bresenham algorithm为计算机图形学中使用像素点显示直线的算法,算法使用整数运算,能大幅提升计算速度。最近概率栅格建图算法中涉及到直线绘制,故推导学习了下。

公式推导

主要是第一象限斜率小于1的区域的公式推导,其他区域可以转换到该区域计算。

1

斜率公式如下,其中d为上图中每移动一个像素,y轴上增加的数值,即斜率
y 1 − y 0 x 1 − x 0 = δ y δ x = d \frac{y_1 - y_0}{x_1 - x_0} = \frac{\delta y}{\delta x} = d x1x0y1y0=δxδy=d
此时判断每次x增长时,纵轴的增值,对第一个像素判断d>0.5,是则y+1,下一个像素则判断2d>1.5,否则y不变,下一个像素则判断2d>0.5。

因为上一个像素没变,对比的阈值依旧保持不变。如上图示例:

x = x 1 = x 0 + 1 x=x_1 = x_0+1 x=x1=x0+1时,d>0.5,则 y 1 = y 0 + 1 y_1=y_0+1 y1=y0+1,y向上移动一个像素。d-0.5>0 => 2*dy-dx > 0

x = x 2 = x 0 + 2 x=x_2 = x_0+2 x=x2=x0+2时,2d<1.5,则 y 2 = y 1 y_2=y_1 y2=y1,y保持不变,此时1.5保持不变 2d-1.5>0 => 2*dy-dx + 2*dy-2*dx<0

x = x 2 = x 0 + 3 x=x_2 = x_0+3 x=x2=x0+3时,3d>1.5,则 y 3 = y 2 + 1 y_3=y_2+1 y3=y2+1,y向上移动一个像素 3d-1.5>0 => 2*dy-dx +2*dy-2*dx + 2*dy>0

递推公式如下:
D k = { 2 ∗ d y − d x k = 0 D k − 1 + 2 ∗ d y k > 0 且 D k − 1 < 0 D k − 1 + 2 ∗ d y − 2 ∗ d x k > 0 且 D k − 1 > 0 D_k = \begin{cases} 2 * dy -dx &k = 0 \\ D_{k-1} + 2*dy & k>0且D_{k-1} < 0 \\ D_{k-1} + 2*dy-2*dx & k>0且D_{k-1} > 0 \\ \end{cases} Dk= 2dydxDk1+2dyDk1+2dy2dxk=0k>0Dk1<0k>0Dk1>0

伪代码如下:

plotLine(x0, y0, x1, y1)
    dx = x1 - x0
    dy = y1 - y0
    D = 2 * dy - dx
    y = y0

    for x from x0 to x1
        plot(x, y)
        if D > 0
            y = y + 1
            D = D - 2*dx
        end if
        D = D + 2*dy

再精简为如下:

plotLine(x0, y0, x1, y1)
    dx = x1 - x0
    dy = y1 - y0
    D = 0
    y = y0

    for x from x0 to x1
        plot(x, y)
        D += dy
        if 2 * D > 0
        	D = D - dx
            y = y + 1
        end if

所有情况讨论

以(x0,y0)为原点,可分为如下8种+平行xy轴的10种情况讨论

2

以下实现以(x0,y0)为原点实现,如果(x0,y0)不是原点,需要先把数据平移到原点,再平移回去,或者修改TransformQuadrant函数

python实现及可视化

3

import matplotlib.pyplot as plt
import numpy as np


# 只支持x0=0,y0=0,要支持其他原点需要先平移到原点,再平移回去
def BresenhamAlgorithm(x0, y0, x1, y1):
    # 1.process parallel situation
    if x0 == x1 and y0 == y1:
        return [x0, y0]
    elif x0 == x1:
        if y0 < y1:
            y_min = y0
            y_max = y1
        else:
            y_min = y1 + 1
            y_max = y0 + 1

        result = []
        for y in range(y_min, y_max):
            result.append([x0, y])
        return result
    elif y0 == y1:
        if x0 < x1:
            x_min = x0
            x_max = x1
        else:
            x_min = x1 + 1
            x_max = x0 + 1
        result = []
        for x in range(x_min, x_max):
            result.append([x, y0])
        return result

    situation = 0
    if x1 > x0 and y1 > y0:
        if (y1 - y0) < (x1 - x0):
            situation = 11
        else:
            situation = 12
    elif x1 < x0 and y1 > y0:
        if (y1 - y0) < (x0 - x1):
            situation = 24
        else:
            situation = 23
    elif x1 < x0 and y1 < y0:
        if (y0 - y1) < (x0 - x1):
            situation = 35
        else:
            situation = 36
    elif x1 > x0 and y1 < y0:
        if (y0 - y1) < (x1 - x0):
            situation = 48
        else:
            situation = 47

    # transform quadrant-2/3/4 to quadrant-1, or transform back
    def Swap(xt, yt):
        tmp = xt
        xt = yt
        yt = tmp
        return xt, yt

    def TransformQuadrant(xt, yt, pose, back=False):
        if pose == 12:
            xt, yt = Swap(xt, yt)
        elif pose == 23:
            xt = -xt
            xt, yt = Swap(xt, yt)
        elif pose == 24:
            xt = -xt
        elif pose == 35:
            xt = -xt
            yt = -yt
        elif pose == 36:
            xt = -xt
            yt = -yt
            xt, yt = Swap(xt, yt)
        elif pose == 47:
            yt = -yt
            xt, yt = Swap(xt, yt)
        elif pose == 48:
            yt = -yt

        if back:
            if pose == 23 or pose == 47:
                xt = -xt
                yt = -yt
        return xt, yt

    # 3. transform to quadrant-1_1
    # print(f"before {x1 - x0}, {y1 - y0}")
    x1, y1 = TransformQuadrant(x1, y1, situation)
    # print(f"after {x1 - x0}, {y1 - y0}")

    # 4. compute grid in line
    delta_x = x1 - x0
    delta_y = y1 - y0
    error = 0
    y = y0
    result = []
    for x in range(x0, x1):
        result.append([x, y])
        error += delta_y
        if 2 * error > delta_x:
            error -= delta_x
            y += 1

    # 5. transform back to original quadrant
    for pos in result:
        pos[0], pos[1] = TransformQuadrant(pos[0], pos[1], situation, True)
    return result


# grid map range
range_max = 70
grid_map = [[0 for i in range(range_max)] for j in range(range_max)]

# 设置x0,y0
xx, yy = 10, 10
# xx, yy = 0, 0
test_pts = [
    [xx - 10, yy], [xx + 10, yy], [xx, yy - 10], [xx, yy + 10],  # 平行轴
    [xx + 15, yy + 10], [xx + 10, yy + 15],  # 第一象限
    [xx - 15, yy + 10], [xx - 10, yy + 15],  # 第二象限
    [xx - 15, yy - 10], [xx - 10, yy - 15],  # 第三象限
    [xx + 15, yy - 10], [xx + 10, yy - 15]  # 第四象限
]

grid_idx = []
move_dis = 20
for pt in test_pts:
    plt.plot([xx + move_dis, pt[0] + move_dis], [yy + move_dis, pt[1] + move_dis])
    res = BresenhamAlgorithm(0, 0, pt[0] - xx, pt[1] - yy)
    for a in res:
        a[0] += xx
        a[1] += yy
        grid_map[a[1] + move_dis][a[0] + move_dis] = 5
        grid_idx.append(a)

print(grid_idx)

plt.imshow(grid_map, cmap=plt.cm.hot, interpolation='nearest', vmin=0, vmax=10)
plt.colorbar()
plt.xlim(0, 20)
plt.ylim(0, 20)
my_x_ticks = np.arange(0, range_max, 1)
my_y_ticks = np.arange(0, range_max, 1)
plt.xticks(my_x_ticks)
plt.yticks(my_y_ticks)
plt.grid(True)
plt.show()

C++实现

#include <iostream>
#include <vector>
#include <gtest/gtest.h>

/**
 * @details implement of bresenham algorithm from https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
 * @param x0
 * @param y0
 * @param x1
 * @param y1
 * @return intersection grid indexes
 */
std::vector<std::pair<int, int>> BresenhamAlgorithm(int x0, int y0, int x1, int y1)
{
	using GridIndex = std::pair<int, int>;

	// 1. process parallel situation
	if (x1 == x0 && y1 == y0)
	{
		return { GridIndex(x0, y0) };
	}
	else if (x1 == x0)
	{
		GridIndex tmp_index;
		std::vector<GridIndex> result;
		result.reserve(static_cast<size_t>(std::abs(y1 - y0)));
		int y_min = 0;
		int y_max = 0;
		if (y0 < y1)
		{
			y_min = y0;
			y_max = y1;
		}
		else
		{
			y_min = y1 + 1;
			y_max = y0 + 1;
		}
		for (int y = y_min; y < y_max; ++y)
		{
			tmp_index.first = x1;
			tmp_index.second = y;
			result.emplace_back(tmp_index);
		}
		return result;
	}
	else if (y1 == y0)
	{
		GridIndex tmp_index;
		std::vector<GridIndex> result;
		result.reserve(static_cast<size_t>(std::abs(x1 - y0)));
		int x_min = 0;
		int x_max = 0;
		if (x0 < x1)
		{
			x_min = x0;
			x_max = x1;
		}
		else
		{
			x_min = x1 + 1;
			x_max = x0 + 1;
		}
		for (int x = x_min; x < x_max; ++x)
		{
			tmp_index.first = x;
			tmp_index.second = y1;
			result.emplace_back(tmp_index);
		}
		return result;
	}

	// situation include eight parts of quadrant(counterclockwise)
	enum class Situation : int8_t
	{
		kQuadrant1_1 = 0,
		kQuadrant1_2 = 2,
		kQuadrant2_3 = 3,
		kQuadrant2_4 = 4,
		kQuadrant3_5 = 5,
		kQuadrant3_6 = 6,
		kQuadrant4_7 = 7,
		kQuadrant4_8 = 8
	};

	// 2. get situation from grid position
	Situation situation = Situation::kQuadrant1_1;
	if (x1 > x0 && y1 > y0)
	{
		situation = ((y1 - y0) < (x1 - x0)) ? Situation::kQuadrant1_1 : Situation::kQuadrant1_2;
	}
	else if (x1 < x0 && y1 > y0)
	{
		situation = ((y1 - y0) < (x0 - x1)) ? Situation::kQuadrant2_4 : Situation::kQuadrant2_3;
	}
	else if (x1 < x0 && y1 < y0)
	{
		situation = ((y0 - y1) < (x0 - x1)) ? Situation::kQuadrant3_5 : Situation::kQuadrant3_6;
	}
	else if (x1 > x0 && y1 < y0)
	{
		situation = ((y0 - y1) < (x1 - x0)) ? Situation::kQuadrant4_8 : Situation::kQuadrant4_7;
	}

	// transform quadrant-2/3/4 to quadrant-1, or transform back
	auto TransformQuadrant = [&](int& x1, int& y1, Situation situation, bool back = false)
	{
	  switch (situation)
	  {
	  case Situation::kQuadrant1_2:
		  std::swap(x1, y1);
		  break;
	  case Situation::kQuadrant2_3:
		  x1 = -x1;
		  std::swap(x1, y1);
		  break;
	  case Situation::kQuadrant2_4:
		  x1 = -x1;
		  break;
	  case Situation::kQuadrant3_5:
		  x1 = -x1;
		  y1 = -y1;
		  break;
	  case Situation::kQuadrant3_6:
		  x1 = -x1;
		  y1 = -y1;
		  std::swap(x1, y1);
		  break;
	  case Situation::kQuadrant4_7:
		  y1 = -y1;
		  std::swap(x1, y1);
		  break;
	  case Situation::kQuadrant4_8:
		  y1 = -y1;
		  break;
	  case Situation::kQuadrant1_1:
	  default:
		  break;
	  }
	  if (back)
	  {
		  if (situation == Situation::kQuadrant2_3 || situation == Situation::kQuadrant4_7)
		  {
			  x1 = -x1;
			  y1 = -y1;
		  }
	  }
	};

	// 3. transform to quadrant-1_1
	TransformQuadrant(x1, y1, situation);
	int delta_x = x1 - x0;
	int delta_y = y1 - y0;
	int error = 0;
	int y = y0;

	// 4. compute grid in line
	GridIndex tmp_index;
	std::vector<GridIndex> result;
	for (int x = x0; x < x1; ++x)
	{
		tmp_index.first = x;
		tmp_index.second = y;
		result.emplace_back(tmp_index);

		error += delta_y;
		if (2 * error > delta_x)
		{
			error -= delta_x;
			y += 1;
		}
	}

	// 5. transform back to original quadrant
	for (auto& grid_index : result)
	{
		TransformQuadrant(grid_index.first, grid_index.second, situation, true);
	}
	return result;
}

TEST(test_bresenham, test_bresenham)
{
	std::vector<std::pair<int, int>> ground_truth
		{{ -9, 0 }, { -8, 0 }, { -7, 0 }, { -6, 0 }, { -5, 0 }, { -4, 0 }, { -3, 0 }, { -2, 0 }, { -1, 0 }, { 0, 0 },
		 { 0, 0 }, { 1, 0 }, { 2, 0 }, { 3, 0 }, { 4, 0 }, { 5, 0 }, { 6, 0 }, { 7, 0 }, { 8, 0 }, { 9, 0 }, { 0, -9 },
		 { 0, -8 }, { 0, -7 }, { 0, -6 }, { 0, -5 }, { 0, -4 }, { 0, -3 }, { 0, -2 }, { 0, -1 }, { 0, 0 }, { 0, 0 },
		 { 0, 1 }, { 0, 2 }, { 0, 3 }, { 0, 4 }, { 0, 5 }, { 0, 6 }, { 0, 7 }, { 0, 8 }, { 0, 9 }, { 0, 0 }, { 1, 1 },
		 { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 3 }, { 6, 4 }, { 7, 5 }, { 8, 5 }, { 9, 6 }, { 10, 7 }, { 11, 7 },
		 { 12, 8 }, { 13, 9 }, { 14, 9 }, { 0, 0 }, { 1, 1 }, { 1, 2 }, { 2, 3 }, { 3, 4 }, { 3, 5 }, { 4, 6 },
		 { 5, 7 }, { 5, 8 }, { 6, 9 }, { 7, 10 }, { 7, 11 }, { 8, 12 }, { 9, 13 }, { 9, 14 }, { 0, 0 }, { -1, 1 },
		 { -2, 1 }, { -3, 2 }, { -4, 3 }, { -5, 3 }, { -6, 4 }, { -7, 5 }, { -8, 5 }, { -9, 6 }, { -10, 7 }, { -11, 7 },
		 { -12, 8 }, { -13, 9 }, { -14, 9 }, { 0, 0 }, { -1, 1 }, { -1, 2 }, { -2, 3 }, { -3, 4 }, { -3, 5 }, { -4, 6 },
		 { -5, 7 }, { -5, 8 }, { -6, 9 }, { -7, 10 }, { -7, 11 }, { -8, 12 }, { -9, 13 }, { -9, 14 }, { 0, 0 },
		 { -1, -1 }, { -2, -1 }, { -3, -2 }, { -4, -3 }, { -5, -3 }, { -6, -4 }, { -7, -5 }, { -8, -5 }, { -9, -6 },
		 { -10, -7 }, { -11, -7 }, { -12, -8 }, { -13, -9 }, { -14, -9 }, { 0, 0 }, { -1, -1 }, { -1, -2 }, { -2, -3 },
		 { -3, -4 }, { -3, -5 }, { -4, -6 }, { -5, -7 }, { -5, -8 }, { -6, -9 }, { -7, -10 }, { -7, -11 }, { -8, -12 },
		 { -9, -13 }, { -9, -14 }, { 0, 0 }, { 1, -1 }, { 2, -1 }, { 3, -2 }, { 4, -3 }, { 5, -3 }, { 6, -4 },
		 { 7, -5 }, { 8, -5 }, { 9, -6 }, { 10, -7 }, { 11, -7 }, { 12, -8 }, { 13, -9 }, { 14, -9 }, { 0, 0 },
		 { 1, -1 }, { 1, -2 }, { 2, -3 }, { 3, -4 }, { 3, -5 }, { 4, -6 }, { 5, -7 }, { 5, -8 }, { 6, -9 }, { 7, -10 },
		 { 7, -11 }, { 8, -12 }, { 9, -13 }, { 9, -14 }};

	std::vector<std::pair<int, int>> test_pts{
		{ -10, 0 }, { 10, 0 }, { 0, -10 }, { 0, 10 },
		{ +15, +10 }, { +10, +15 },
		{ -15, +10 }, { -10, +15 },
		{ -15, -10 }, { -10, -15 },
		{ +15, -10 }, { +10, -15 }
	};

	std::vector<std::pair<int, int>> results;
	for (auto& pt : test_pts)
	{
		auto res = BresenhamAlgorithm(0, 0, pt.first, pt.second);
		results.insert(results.end(), res.begin(), res.end());
	}
	EXPECT_EQ(results, ground_truth);
	// for (auto& pt : results)
	// {
	// 	printf("[%d, %d], ", pt.first, pt.second);
	// }
	// printf("\n");
}
int main(int argc, char** argv)
{
	::testing::InitGoogleTest(&argc, argv);
	return RUN_ALL_TESTS();

	return 0;
}
cmake_minimum_required(VERSION 3.21)
project(test_bresenham)

set(CMAKE_CXX_STANDARD 14)

add_executable(test_bresenham main.cpp)
target_link_libraries(test_bresenham gtest pthread)

参考

  • https://www.bilibili.com/video/BV1364y1d7Lo/?vd_source=9408e8cab54b943547dbc522a9112342
  • https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
  • https://www.cs.montana.edu/courses/spring2009/425/dslectures/Bresenham.pdf

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

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

相关文章

CloudCanal实战-Oracle数据迁移同步到PostgreSQL

简述 本篇文章主要介绍如何使用 CloudCanal 构建一条 Oracle 到 PostgreSQL 的数据同步链路 技术要点 缩小的数据库权限要求 CloudCanal 对 Oracle 数据库的高权限要求&#xff0c;主要来自两个面向 DBA 的操作&#xff0c;自动构建字典和 自动切换归档日志&#xff0c;这两…

详解 strtok 函数以及模拟实现

目录 一、strtok 函数的介绍 二、strtok 函数的模拟实现 一、strtok 函数的介绍 函数原型&#xff1a; char* strtok(char* str, const char* delimiters); delimiter n.[计]分隔符&#xff0c;定界符&#xff08;a character that marks the beginning or end of a unit o…

KMP算法详解+动图演示

目录 一、KMP算法简介 二、KMP算法的详细图解 1. 先了解BF算法的基本思路 2. 简单了解KMP算法 3. next数组的引入 4. next数组的代码实现&#xff08;含动态演示&#xff09; 三、KMP算法完整代码 一、KMP算法简介 KMP算法是一种改进的字符串匹配算法&#xff0c;由 …

【算法】二分图判定

目录1.概述2.代码实现3.应用本文参考&#xff1a; LABULADONG 的算法网站 1.概述 &#xff08;1&#xff09;二分图 (Bipartite Graph)&#xff0c;又称为二部图&#xff0c;是图论中的一种特殊模型。 设 G (V, E) 是一个无向图&#xff0c;如果顶点 V 可分割为两个互不相交的…

Unity 和vs2022对接问题

第一个问题&#xff1a;在vs中编写好的程序在unity中预览出现乱码&#xff1b;提示&#xff1a;只要是乱码的问题90%离不开编码表Unity中的编码表是utf-8,而vs中默认的应该是GB2312。英文还好&#xff0c;中文可定就会出现乱码&#xff0c;解决方法也很简单&#xff1a;把vs中的…

【Python基础四】入门级朋友看的超详教程

前言 这是最后一篇基础的文章啦 往期文章&#xff1a; 【Python基础一】入门级朋友看的超详教程 【Python基础二】入门级朋友看的超详教程 【Python基础三】入门级朋友看的超详教程 刚开始接触Python的宝子&#xff0c;有什么不懂的都可以私信我哦 我还准备了大量的免费…

目标检测:YOLO V2思路解读

目标检测&#xff1a;YOLO V2思路解读YOLO V1存在的问题主要改进Batch NormalizationHigh Resolution ClassifierConvolutional With Anchor BoxesDimension ClusterDirect location PredictionFine-Grained FeaturesMulti-Scale TrainingLoss FunctionYOLO V1存在的问题 对于…

使用Redis代替Session实现短信登陆

1.集群的Session共享问题 多台Tomcat并不共享Session存储空间&#xff0c;当请求切换到不同tomcat服务器时会导致数据丢失&#xff1a; 当用户量增多&#xff0c;我们需要进行负载均衡、对tomcat做水平扩展&#xff0c;可是存储在Tomcat里的Session不是共享的&#xff0c;这…

从C和C++内存管理来谈谈JVM的垃圾回收算法设计-上

从C和C内存管理来谈谈JVM的垃圾回收算法设计-上引言C内存模型malloc堆内存分配过程malloc为什么结合使用brk和mmapmalloc如何通过内存池管理Heap区域垃圾收集器引言 本文想和大家来探讨一下JVM是如何对堆内存进行管理和垃圾回收,相关书籍如深入理解JVM第三版中已经介绍过了相关…

OSCP-Vulnhub靶机记录-digitalworldlocal-fall

Vulnhub靶机记录-digitalworldlocal-fall靶机描述安装扫描枚举使用kali自带的FUZZ权限提升靶机描述 靶机地址&#xff1a;https://www.vulnhub.com/entry/digitalworldlocal-fall,726/ Description To celebrate the fifth year that the author has survived his infosec ca…

也来聊聊滑块验证码的那些事

单位做攻防演习&#xff0c;我扮演攻击方尝试破解。发现滑块验证码做了升级&#xff0c;比之前复杂了很多。好在仍然是一维验证&#xff0c;不用太麻烦。https接口里读出的是json对象&#xff0c;先从对象里取出图片转的base64编码&#xff0c;然后把字符串转回成numpy.ndarray…

Verilog HDL 基础语法

一、逻辑值 0: 逻辑低电平&#xff0c;条件为假 1: 逻辑高电平&#xff0c;条件为真 z: 高阻态&#xff0c;无驱动 x: 未知逻辑电平二、实际例子 1. 模块名一般与文件名相同 线网型变量会被映射成一条真实存在的物理连线。 寄存器型变量会被映射成一个寄存器。 2. 参数 para…

2、JavaScript快速入门

2.1 引入JavaScript 内部标签 <!-- 在script标签内写JavaScript(简称js)代码&#xff0c;代码块可以放在head中&#xff0c;也可以放在body中--> <script>// alert:弹窗alert(Hello,world!); //注意以分号结尾 </script>外部引入 hello.js alert(Hello,worl…

分享120个ASP源码,总有一款适合您

ASP源码 分享120个ASP源码&#xff0c;总有一款适合您 链接&#xff1a;https://pan.baidu.com/s/1WwTsUTLS_qLvP-TC1w-1vQ?pwdvxpk 提取码&#xff1a;vxpk 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c;大家下载…

OB0207 obsidian 自动获取url链接:auto-link-title插件使用

序号解读&#xff1a; 01——软件基础使用、基础语法 02——插件使用 03——综合实战 0 写在前面 Ob社区插件汇总&#xff1a;Airtable - OB社区插件汇总 - Johnny整理 - 每周更新 - B站 Johnny学Explore the "OB社区插件汇总 - Johnny整理 - 每周更新 - B站 Johnny学&qu…

过去一年渲染了3亿帧,助力了63.81亿票房、1150亿播放量丨瑞云渲染年度大事记

2022年&#xff0c;注定是充满未知和挑战的一年。抗疫三年&#xff0c;终于在2022年底迎来放开&#xff0c;我们怀着忐忑的心情告别了核酸、行程码和封控&#xff0c;成为了自己健康的第一负责人。这段时间大家应该都忙着和病毒做斗争吧&#xff0c;瑞云各个岗位的小伙伴们也都…

6.7、万维网(如HTTP超文本传输协议)

1、基本介绍 万维网 WWW (World Wide Web&#xff09;并非某种特殊的计算机网络\color{red}并非某种特殊的计算机网络并非某种特殊的计算机网络。 它是一个大规模的、联机式的信息储藏所&#xff0c;是运行在因特网上的一个分布式应用。 万维网利用网页之间的超链接\color{r…

Web进阶:Day5 移动适配、rem、less

Web进阶&#xff1a;Day5 Date: January 10, 2023 Summary: 移动适配、rem、less 移动适配 移动适配指网页元素的宽高都要随着设备宽度等比缩放 rem &#xff1a; 目前多数企业在用的解决方案 vw / vh&#xff1a;未来的解决方案 rem 目标&#xff1a;能够使用rem单位设置网…

2022年跨境物流指数研究报告

第一章 行业概况 指提供跨境物流服务的行业。跨境物流是指在电子商务环境下&#xff0c;依靠互联网、大数据、信息化与计算机等先进技术&#xff0c;物品从跨境电商企业流向跨境消费者的跨越不同国家或地区的物流活动。 图 物流运输行业产业链结构图 资料来源&#xff1a;资产…

Tapdata Cloud 场景通关系列:集成阿里云计算巢,实现一键云上部署真正开箱即用

【前言】作为中国的 “Fivetran/Airbyte”, Tapdata Cloud 自去年发布云版公测以来&#xff0c;吸引了近万名用户的注册使用。应社区用户上生产系统的要求&#xff0c;Tapdata Cloud 3.0 将正式推出商业版服务&#xff0c;提供对生产系统的 SLA 支撑。Tapdata 目前专注在实时数…