圆角矩形的绘制和曲线均匀化

news2025/7/6 4:33:42

摘要: 圆角矩形是软件 UI 等视觉设计中的常见表达,一种常见的绘制方法是将矩形的四角替换为与边相切的四分之一圆弧,然而这种绘制方式会在连接处产生视觉上的切折感,这是因为圆弧和直线的连接处只满足 G 1 G^1 G1 连续性。本文探究了如何使用高次埃米尔特插值绘制高阶连续的圆角曲线,以及如何使用常微分方程的数值解法实现曲线的均匀化。
关键词: 圆角矩形;埃米尔特插值;常微分方程数值解


引言

圆角矩形被广泛应用于产品的视觉设计中,例如智能手机的边框、移动应用的图标、家具的边角等等。人们通常将在矩形的四角改为相切的四分之一圆弧来绘制圆角矩形,如图 1a 所示,在和更高阶连续曲线的对比之下,这种方法绘制出的圆角就会在连接处产生切折感,显得不是那么顺滑,原因是这种绘制方式在圆角的连接处只能满足 G 1 G^1 G1 连续,图 1b 直观展示了连接处的曲率突变。这种视觉效果的差别已经被 Apple 公司的视觉设计师注意到,并且成功应用于他们的产品设计中,例如 iOS 7 之后的图标边框抛弃了 圆弧和切线(图 2)。

图1(a):效果对比图1(b):曲率图
请添加图片描述请添加图片描述
图2
请添加图片描述

根据数值分析的内容,通过埃米尔特插值可以构造任意高阶的多项式,从而可以满足任意高阶的连续性要求,所以我们就可以将其应用于圆角的绘制,从而得到更顺滑的圆角曲线。


问题建模

由于二维曲线上可能存在相同横坐标的点,所以一般情况下不能用形如 y = f ( x ) y=f(x) y=f(x) 这样的函数表达,我们可以通过引入一个隐变量 t t t 来表达任意的二维曲线:
{ P x = x ( t ) P y = y ( t ) \begin{equation} \left\{\begin{array}{cc} P_x = x(t) \\ P_y = y(t) \end{array}\right. \end{equation} {Px=x(t)Py=y(t)
隐变量 t t t 可以理解为绘制过程的时间,因此公式实际上也描述了曲线的画法:对指定的时间点 t t t,可以求得要绘制的点 P = ( P x , P y ) = ( x ( t ) , y ( t ) ) P = (P_x, P_y) = (x(t), y(t)) P=(Px,Py)=(x(t),y(t))。 例如图 1a 中的圆弧和切线组合可以表达为:
P = { ( 0 , t ) t < 0 ( 1 − cos ⁡ ( π 2 t ) , sin ⁡ ( π 2 t ) ) 0 ≤ t < 1 ( t , 1 ) 1 ≤ t \begin{equation} P = \begin{cases} (0, t) & t < 0 \\ (1 - \cos(\frac{\pi}{2} t), \sin(\frac{\pi}{2} t)) & 0 \le t < 1 \\ (t, 1) & 1 \le t \end{cases} \end{equation} P= (0,t)(1cos(2πt),sin(2πt))(t,1)t<00t<11t

在数学上, x ( t ) x(t) x(t) y ( t ) y(t) y(t) k k k 阶导数连续被称为参数连续性, 使用 C k C^k Ck 来表示。然而参数连续性并不能完全表达曲线的连续性,因为相同
的轨迹可能由完全不同的运动过程形成,例如下面的公式和式 2 描述了同样的轨迹,但不满足 C 1 C^1 C1 连续性:
P = { ( 0 , t ) t < 0 ( 1 − cos ⁡ ( π 2 t ) , sin ⁡ ( π 2 t ) ) 0 ≤ t < 1 ( 1 , 1 ) 1 ≤ t < 2 ( t − 1 , 1 ) 2 ≤ t \begin{equation} P = \begin{cases} (0, t) & t < 0 \\ (1 - \cos(\frac{\pi}{2} t), \sin(\frac{\pi}{2} t)) & 0 \le t < 1 \\ (1, 1) & 1 \le t < 2 \\ (t -1, 1) & 2 \le t \end{cases} \end{equation} P= (0,t)(1cos(2πt),sin(2πt))(1,1)(t1,1)t<00t<11t<22t

因此,几何连续性( G k G^k Gk)的概念被提出,其含义是位置 P = ( x ( t ) , y ( t ) ) P=(x(t), y(t)) P=(x(t),y(t)) 关于路程 s = ∫ 0 t x ′ 2 ( t ) + y ′ 2 ( t )   d t s=\int_0^t { \sqrt{x'^2(t) + y'^2(t)}\ \mathrm{d}t } s=0tx′2(t)+y′2(t)  dt
的参数方程(称为自然参数方程)满足 k k k 阶导数 连续的要求。一般来说,如果一个曲线 P = ( x ( t ) , y ( t ) ) P=(x(t), y(t)) P=(x(t),y(t)) 可以经过一个重参数化过程 P = ( x ( t ( s ) ) , y ( t ( s ) ) ) P=(x(t(s)), y(t(s))) P=(x(t(s)),y(t(s))) 变成 k k k 阶参数连续的,那么它就是 k k k 阶几何连续的。如果 x ( t ) x(t) x(t) y ( t ) y(t) y(t) 都是满足 C k C^k Ck 的多项式,那么根据经验易知,我们总可以经过一个非线性的时间放缩过程得到其对应的自然参数方程,因而它也是满足 G k G^k Gk 的。

不失一般性,下面我们只讨论从 ( 0 , 0 ) (0,0) (0,0) ( 1 , 1 ) (1,1) (1,1) 的圆角绘制问题,并且不妨限定转角的绘制时间为 [ 0 , 1 ] [0, 1] [0,1]。在这个问题中, x ( t ) x(t) x(t) y ( t ) y(t) y(t) 要满足以下约束:

  1. 起终点位置
    x ( 0 ) = 0 ,   y ( 0 ) = 0 , x ( 1 ) = 1 ,   y ( 1 ) = 1 \begin{equation} x(0) = 0,\ y(0) = 0, x(1) = 1,\ y(1) = 1 \end{equation} x(0)=0, y(0)=0,x(1)=1, y(1)=1

  2. G 1 G^1 G1 连续
    圆角的起点和终点都与直线相接:
    x ′ ( 0 ) = 0 ,   y ′ ( 0 ) = k y ,   x ′ ( 1 ) = k x ,   y ′ ( 1 ) = 0 \begin{equation} x'(0) = 0,\ y'(0) = k_y,\ x'(1) = k_x,\ y'(1) = 0 \end{equation} x(0)=0, y(0)=ky, x(1)=kx, y(1)=0
    式中出现 k y k_y ky k x k_x kx 的原因是直线 L = ( a t , b t ) L=(at, bt) L=(at,bt) 乘上一个系数后的结果 L ′ = ( k ⋅ a t , k ⋅ b t ) L'=(k \cdot at, k \cdot bt) L=(kat,kbt) 形成的图像与此前相同(即对应同样的自然参数方程)。

  3. G n   ( n > 1 ) G^n\ (n>1) Gn (n>1) 连续
    在曲线内部, n n n 阶多项式本身即满足 G n G^n Gn 的要求。在端点处,因为直线二阶以上的导数都为 0 0 0,所以:
    x ( n ) ( 0 ) = y ( n ) ( 0 ) = x ( n ) ( 1 ) = y ( n ) ( 1 ) = 0 \begin{equation} x^{(n)}(0) = y^{(n)}(0) = x^{(n)}(1) = y^{(n)}(1) = 0 \end{equation} x(n)(0)=y(n)(0)=x(n)(1)=y(n)(1)=0

  4. 关于角平分线对称
    绘制的圆角应该关于角的平分线 x + y = 1 x+y=1 x+y=1 对称:
    ∀ t ∈ [ 0 , 1 ] , ∃ t ′ ∈ [ 0 , 1 ] { x ( t ) + x ( t ′ ) 2 + y ( t ) + y ( t ′ ) 2 = 1 y ( t ′ ) − y ( t ) = x ( t ′ ) − x ( t ) \begin{equation} \forall t \in [0, 1], \exists t' \in [0, 1] \left\{ \begin{aligned} & \frac{x(t) + x(t')}{2} + \frac{y(t) + y(t')}{2} = 1 \\ & y(t') - y(t) = x(t') - x(t) \end{aligned} \right. \end{equation} t[0,1],t[0,1] 2x(t)+x(t)+2y(t)+y(t)=1y(t)y(t)=x(t)x(t)


圆角的绘制

我们可以通过一个更严格的充分条件保证我们设计的函数一定满足约束 4:让绘制过程也关于这条直线对称的, 即 t ′ = 1 − t t' = 1 - t t=1t,那么我们就可以推出:
y ( t ) = 1 − x ( 1 − t ) k y = k x \begin{equation} \begin{aligned} y(t) & = 1 - x(1 - t) \\ k_y & = k_x \end{aligned} \end{equation} y(t)ky=1x(1t)=kx
现在,我们只需确定 x ( t ) x(t) x(t) k x k_x kx 即可,这极大简化了设计工作。设计满足约束 1-3 的函数 x ( t ) x(t) x(t) 本质上是一个插值问题,满足 G n G^n Gn
的圆角曲线有 2 n + 2 2n+2 2n+2 个插值条件,因此可以用 2 n + 1 2n+1 2n+1 次埃米尔特插值构造 x ( t ) x(t) x(t)

n = 2 n=2 n=2 为例,构造均差表 1a,可得插值多项式:
x ( t ) = t 3 + ( k x − 3 ) t 3 ( t − 1 ) + ( 6 − 3 k x ) t 3 ( t − 1 ) 2 \begin{equation} \begin{aligned} x(t) = t^3 + (k_x - 3) t^3 (t-1) + (6 - 3k_x) t^3 (t-1)^2 \end{aligned} \end{equation} x(t)=t3+(kx3)t3(t1)+(63kx)t3(t1)2
图 3 显示了不同 k x k_x kx 取值曲线的效果,从图上可见, k x k_x kx 值的选取对效果有非常重要的影响,而当 k x = 2 k_x=2 kx=2 时曲线的视觉效果最顺滑。

很难去解释为什么 k x = 2 k_x=2 kx=2 时的曲线效果最好,但观察式 9 可知 k x = 2 k_x=2 kx=2 恰好使得中最高次项系数为 0, 此时插值多项式退化为 2 n 2n 2n 次多项式,这不得不让人怀疑这不是一种巧合。我们有理由猜测,在求更高阶连续的曲线时,同样当 k x k_x kx 值的选取使得最高次项系数为 0 时效果最好。

图 3
请添加图片描述

请添加图片描述
顺着这个猜测会发现一个更有趣的事实:对于任意的高阶连续的曲线 n > 1 n > 1 n>1,使它们最高次项系数为 0 的条件都是
k x = 2 k_x = 2 kx=2!可以这样简单证明这个结论:把 k x = 2 k_x = 2 kx=2 带入表 1a 得到表 1b。当 n n n 由 2 变为 3 会多出两个插值条件,反应到均差表的构造上就是会多出表 1c 中的第 4 行和第 8 行。 在表 1b 中,第 3 行的插值条件往右下和第 6 行的插值条件往右的数字刚好满足"绝对值相等、正负号交替"的性质,则根据均差表的构造规则,在表 1c 中新构造出的一层也满足这个性质,最终右下角的数字一定为两个符号和绝对值相同的数的差,因此一定为 0。

图 4 是 n n n k x k_x kx 取不同值的效果对比,可明显看出, k x = 2 k_x=2 kx=2 在任何阶次下的效果都是最好的。当可以绘制任意高阶连续的圆角之后,一个很自然的问题是:能否绘制无穷阶连续的圆角?答案是否定的,因为无穷阶连续会使得 x ( t ) x(t) x(t) 的任意阶导数都为 0,那么根据 x ( 0 ) = 0 x(0)=0 x(0)=0,使用泰勒插值可得 x ( t ) ≡ 0 x(t) \equiv 0 x(t)0,这与 x ( 1 ) = 1 x(1)=1 x(1)=1 冲突。从图上也能直观看出,更高阶的曲线则产生更小的圆角,而当无穷阶连续时就会退化为直角。

图4
请添加图片描述

曲线均匀化

上一节给出的方法已经能让我们绘制出效果很好的连续圆角曲线了,但其仍有不足之处:所得插值公式在输入均匀的 t t t 时输出的点是不均匀的。这个问题在绘制虚线或者点线等不连续的线时就会显露出来,如图5,点会在拐角处更集中,因此有必要继续探索将曲线均匀化的方法。

图5
请添加图片描述

本质上,曲线均匀化就是从上面一节中所得的参数方程求得对应的自然参数方程的过程。自然参数方程在大多
数情况下都很难求,无法得到解析解,只能使用数值方法,在这里也不例外。我们可以通过一个非线性的重参
数化来实现均匀化,即设计一个函数 t ( s ) t(s) t(s),使得 P = ( x ( t ( s ) ) , y ( t ( s ) ) ) P=(x(t(s)), y(t(s))) P=(x(t(s)),y(t(s))) 满足:
( d P x d s ) 2 + ( d P y d s ) 2 = 1 \begin{equation} \begin{aligned} \sqrt{ \left(\frac{\mathrm{d}P_x}{\mathrm{d}s}\right)^2 + \left(\frac{\mathrm{d}P_y}{\mathrm{d}s}\right)^2 } = 1 \end{aligned} \end{equation} (dsdPx)2+(dsdPy)2 =1
将式 8 代入得:
d P x d s 2 + d P y d s 2 = ( x ′ ( t ( s ) ) ⋅ t ′ ( s ) ) 2 + ( x ′ ( 1 − t ( s ) ) ⋅ t ′ ( s ) ) 2 = 1 \begin{equation} \frac{\mathrm{d}P_x}{\mathrm{d}s}^2 + \frac{\mathrm{d}P_y}{\mathrm{d}s}^2 = \left(x'(t(s)) \cdot t'(s) \right)^2 + \left(x'(1 - t(s)) \cdot t'(s) \right)^2 = 1 \end{equation} dsdPx2+dsdPy2=(x(t(s))t(s))2+(x(1t(s))t(s))2=1
整理后,可得关于 t ( s ) t(s) t(s) 的微分方程:
t ′ ( s ) = 1 x ′ 2 ( t ( s ) ) + x ′ 2 ( 1 − t ( s ) ) \begin{equation} t'(s) = \frac{1}{ \sqrt{x'^2(t(s)) + x'^2(1 - t(s))} } \end{equation} t(s)=x′2(t(s))+x′2(1t(s)) 1
其中开方后的符号对曲线的最终均匀化结果没有影响,故可以取正。

针对本圆角绘制问题,该微分方程的初值条件为 t ( 0 ) = 0 t(0) = 0 t(0)=0。绘制均匀化圆角的完整流程如下:

  1. 使用上一节的方法求出多项式 x ( t ) x(t) x(t)
  2. 求出 x ( t ) x(t) x(t) 的导数 x ′ ( t ) x'(t) x(t),很容易编程实现多项式的求导;
  3. 给定步长 s s s,利用式 12 和初值条件 t ( 0 ) = 0 t(0)=0 t(0)=0 求得均匀分布的间隔为 s s s 的点对应的 t t t 值;
  4. 将求得的 t t t 值序列代入 x ( t ) x(t) x(t) 和式 8,得到最终的点坐标。

图 6b 为在 n = 2 n=2 n=2 k x = 2 k_x=2 kx=2 的圆角曲线上取 s = 0.05 s=0.05 s=0.05 的绘制效果,图 6a 为对应的 t t t 值序列,从图中可以明显看出是非线性的。

图6
请添加图片描述

总结

由于研究时间有限,本文只讨论了最常见的 90° 直角的绘制方法,但显然可以在本文的方法上进行一些修补使之进一步推广到任意角度。总的来说,本文基于数值分析课程内容,对圆角矩形的绘制方法进行了深入研究,成功绘制出了高阶连续的均匀圆角曲线,并通过均匀化得到了虚线版本,所得曲线相比较普通的圆弧和切线组合具有更顺滑的视觉效果。附录A展示了不同阶连续的完整矩形及点线版本的效果。


附录

A 完整的圆角矩形图像

请添加图片描述

B 圆角矩形绘制代码

  • numalgo.py

    """埃米尔特插值实现
    """
    
    import numpy as np
    from typing import Callable
    
    
    class Polynomial:
        """多项式函数类"""
    
        def __init__(self, a: np.ndarray) -> None:
            self.a = a
    
        def __call__(self, x):
            return self.a @ np.full_like(self.a, x) ** np.arange(0, len(self.a))
    
        def derivate(self) -> "Polynomial":
            """求导一次"""
            return Polynomial(self.a[1:] * np.arange(1, len(self.a)))
    
    
    class Emmert:
        """基于均差表的埃米尔特插值函数类"""
    
        def __init__(self, x: np.ndarray, y: np.ndarray) -> None:
            assert x.shape == y.shape
            self.x = x
            self.y = y
    
        def __call__(self, x):
            x = x - self.x
            for i in range(1, len(x)):
                x[i] *= x[i - 1]
            x[1:] = x[:-1]
            x[0] = 1
            return self.y @ x
    
        def to_polynomial(self) -> Polynomial:
            """均差表转多项式"""
            a = np.zeros_like(self.y)
            b = np.zeros_like(self.y)
            b[0] = 1
            a += self.y[0] * b[0]
            for i in range(1, len(self.y)):
                b[1:] = b[:-1]
                b[0] = 0
                b[:-1] += -self.x[i - 1] * b[1:]
                a += self.y[i] * b
            return Polynomial(a)
    
    
    def emmert(constraits: dict[float, list[float]]) -> Emmert:
        """埃米尔特插值"""
    
        px, py = zip(*sorted(constraits.items()))
        indexes = [len(i) for i in py]
        indexes = [sum(indexes[:i]) for i in range(len(indexes) + 1)]
    
        x = np.empty(indexes[-1], dtype=np.float64)
        for i in range(0, len(indexes) - 1):
            x[indexes[i] : indexes[i + 1]] = px[i]
    
        y = np.empty_like(x)
        for i in range(0, len(x) - 1):
            for j in range(0, len(indexes) - 1):
                if len(py[j]) > i:
                    y[indexes[j] + i : indexes[j + 1]] = py[j][i]
    
            # meandiff
            step = i + 1
            y[step:] = (y[step:] - y[step - 1 : -1]) / (x[step:] - x[:-step])
    
        return Emmert(x, y)
    
    
    def euler(
        deri: Callable[[float, float], float], xs: np.ndarray, y0: float
    ) -> np.ndarray:
        """欧拉法求微分方程数值解"""
    
        ans = np.empty_like(xs)
        ans[0] = y0
    
        for i in range(1, len(xs)):
            ans[i] = ans[i - 1] + (xs[i] - xs[i - 1]) * deri(xs[i - 1], ans[i - 1])
        return ans
    
    
    def euler_range(
        deri: Callable[[float, float], float],
        x0: float,
        y0: float,
        stop: float,
        step: float,
    ) -> np.ndarray:
        """欧拉法求微分方程数值解,均匀步长当型循环"""
    
        ans = []
        while y0 < stop:
            ans.append(y0)
            y0 += step * deri(x0, y0)
            x0 += step
        return np.array(ans)
    
  • a4_roundrect.py

    """绘制完整的圆角矩形"""
    
    import matplotlib as mpl
    import matplotlib.pyplot as plt
    import numpy as np
    import sympy as sy
    from numpy import cos, pi, sin
    
    from numalgo import emmert, euler_range
    
    mpl.rcParams["font.family"] = "Microsoft YaHei"
    
    fig, ax = plt.subplots(1, 1, layout="constrained")
    fig.set_size_inches(8, 10)
    ax.axis("off")
    ax.set_xlim([-8, 8]), ax.set_ylim([-11, 11])
    
    
    def mirror_lefttop(arr_x, arr_y):
        x = np.concatenate((arr_x, -arr_x[::-1], -arr_x, arr_x[::-1]))
        y = np.concatenate((arr_y, arr_y[::-1], -arr_y, -arr_y[::-1]))
        return x, y
    
    
    # Gn 插值
    for n in range(2, 8):
        pf = emmert({0: [0, 0] + [0] * (n - 1), 1: [1, 2] + [0] * (n - 1)})
        p0 = pf.to_polynomial()
    
        p1 = p0.derivate()
        tt = lambda x, y: 1 / np.sqrt(p1(y) ** 2 + p1(1 - y) ** 2)
    
        ts = np.concatenate(
            (
                np.arange(-0.4, 1.3 - n * 1.5 + 0.15, -0.15)[::-1],
                euler_range(tt, 0, 0, 1, 0.15),
                np.arange(1.1, n + 0.1, 0.15),
            )
        )
        ts0, ts1 = np.sum(ts < 0), np.sum(ts < 1)
        xs = ts.copy()
        xs[:ts0] = 0
        ys = ts.copy()
        ys[ts1:] = 1
        for ti in range(ts0, ts1):
            xs[ti] = p0(ts[ti])
    
        ys[ts0:ts1] = 1 - xs[ts1 + 1 : ts0 + 1 : -1]
        xs, ys = mirror_lefttop(xs - n, ys + n * 1.5 - 1.5)
        ax.scatter(xs, ys, 5)
    
    
        arr_t = np.linspace(-n * 1.5 + 0.5, n - 0.5, 1000)
        t0, t1 = np.sum(arr_t < 0), np.sum(arr_t < 1)
        arr_x, arr_y = arr_t.copy(), arr_t.copy()
        arr_x[:t0], arr_y[t1:] = 0, 1
    
        for ti in range(t0, t1):
            arr_x[ti] = p0(arr_t[ti])
        arr_y[t0:t1] = 1 - arr_x[t1 + 1 : t0 + 1 : -1]
        xs, ys = mirror_lefttop(arr_x - n - 0.5, arr_y + n * 1.5 - 1.25 + 0.5)
        ax.plot(xs, ys)
    
    
        ax.text(
            0,
            n * 1.5 + 0.1,
            rf"$G^{n}$",
            horizontalalignment="center",
            verticalalignment="top",
            fontsize=16,
        )
    
    # 圆形是无穷阶几何连续的
    arr_t = np.arange(0, pi * 2, 0.01)
    xs = cos(arr_t)
    ys = sin(arr_t)
    ax.plot(xs, ys)
    ax.text(
        0,
        0,
        r"$G^\infty$",
        horizontalalignment="center",
        verticalalignment="center",
        fontsize=16,
    )
    
    # G0 画圆 + 切线
    arr_t = np.linspace(-1.25, 1.5, 1000)
    t0, t1 = np.sum(arr_t < 0), np.sum(arr_t < 1)
    arr_x, arr_y = arr_t.copy(), arr_t.copy()
    arr_x[:t0], arr_y[t1:] = 0, 1
    
    t = arr_t[t0:t1]
    x = 1 - cos(t)
    arr_x[t0:t1] = x
    arr_y[t0:t1] = 1 - x[::-1]
    
    xs, ys = mirror_lefttop(arr_x - 1.5, arr_y + 1.25)
    ax.plot(xs, ys)
    ax.text(
        0,
        2.1,
        r"$G^1$",
        horizontalalignment="center",
        verticalalignment="top",
        fontsize=16,
    )
    
    fig.savefig("a4_roundrect.png")
    

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

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

相关文章

【刷题篇】链表(下)

前言&#x1f338;各位读者们好&#xff0c;本期我们来填填之前留下的坑&#xff0c;继续来讲解几道和链表相关的OJ题。但和上期单向链表不一样的是&#xff0c;我们今天的题目主要是于环形链表有关&#xff0c;下面让我们一起看看吧。&#x1f4bb;本期的题目有&#xff1a;环…

vue3-element-admin搭建

vue3-element-admin 是基于 vue-element-admin 升级的 Vue3 Element Plus 版本的后台管理前端解决方案&#xff0c;是 有来技术团队 继 youlai-mall 全栈开源商城项目的又一开源力作功能清单技术栈清单技术栈 描述官网Vue3 渐进式 JavaScript 框架 https://v3.cn.vuejs.org/Ty…

IDEA 开发一个简单的 web service 项目,并打包部署到 Tomcat

文章目录实现的效果一、创建 web service 项目二、测试类运行 web service 服务端三、IDEA 打包 web service 项目四、web service 项目部署到 Tomcat五、web service 客户端总结实现的效果 通过 IDEA 创建一个简单的 web service 项目 用测试类运行这个 web service 项目 通…

软件测试面试-一线大厂必问的测试思维面试题

五、测试思维5.1 打电话功能怎么去测&#xff1f;我们会从几个方面去测试&#xff1a;界面、功能、兼容性、易用性、安全、性能、异常。1&#xff09;界面我们会测试下是否跟界面原型图一致&#xff0c;考虑浏览器不同显示比例&#xff0c;屏幕分辨率。2&#xff09;功能&#…

【Linux】环境变量,命令行参数,main函数三个参数保姆教学

目录 ☃️1.奇奇怪怪的现象和孤儿进程 ☃️2.环境变量 ☃️3.深刻理解main函数的前两个参数和命令行参数 ☃️1.奇奇怪怪的现象和孤儿进程 首先回顾一下之前我们学过的fork&#xff08;&#xff09;创建子进程 fork(void)的返回值有两种 注意fork&#xff08;&#xff09;头…

C#:Krypton控件使用方法详解(第八讲) ——kryptonBreadCrumb

今天介绍的Krypton控件中的kryptonBreadCrumb&#xff0c;下面开始介绍这个控件的属性&#xff1a;首先要介绍的是RootItem属性和外观属性&#xff1a;RootItem属性组中包含属性如下&#xff1a;image属性&#xff1a;代表在文字对象的前方插入一个图片&#xff0c;属性值如下图…

多元化业务布局全面开花,松井股份有望步入高成长正轨

2022年&#xff0c;宏观经济下行压力加大、疫情反复不断、国内消费需求不足等诸多因素&#xff0c;导致国内涂料行业遭遇“至暗时刻”&#xff0c;企查查平台数据显示&#xff0c;2022年1-10月&#xff0c;破产重整、注销拍卖的涂料企业超过700家。2月11日&#xff0c;国内涂料…

使用 PyTorch+LSTM 进行单变量时间序列预测(附完整源码)

时间序列是指在一段时间内发生的任何可量化的度量或事件。尽管这听起来微不足道&#xff0c;但几乎任何东西都可以被认为是时间序列。一个月里你每小时的平均心率&#xff0c;一年里一只股票的日收盘价&#xff0c;一年里某个城市每周发生的交通事故数。 在任何一段时间段内记…

代码随想录算法训练营第四天 | 链表理论基础、4. 两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交、142.环形链表II

打卡第四天&#xff0c;因为科三前几天没有带电脑&#xff0c;现在重新补一下第四天的卡 今日任务 24. 两两交换链表中的节点19.删除链表的倒数第N个节点面试题 02.07. 链表相交142.环形链表II 24. 两两交换链表中的节点 给你一个链表&#xff0c;两两交换其中相邻的节点&#…

C++类和对象_02----对象模型和this指针

目录C对象模型和this指针1、成员变量和成员函数分开存储1.1、空类大小1.2、非空类大小1.3、结论2、this指针概念2.1、解决名称冲突2.2、在类的非静态成员函数中返回对象本身&#xff0c;可使用return *this2.3、拷贝构造函数返回值为引用的时候&#xff0c;可进行链式编程3、空…

Python加密算法种类以及开发场景中的运用

不用说火爆一时&#xff0c;全网热议的Web3.0区块链技术&#xff0c;也不必说诸如微信支付、支付宝支付等人们几乎每天都要使用的线上支付业务&#xff0c;单是一个简简单单的注册/登录功能&#xff0c;也和加密技术脱不了干系&#xff0c;本次我们耙梳各种经典的加密算法&…

【RecBole-GNN/源码】RecBole-GNN中lightGCN源码解析

如果觉得我的分享有一定帮助&#xff0c;欢迎关注我的微信公众号 “码农的科研笔记”&#xff0c;了解更多我的算法和代码学习总结记录。或者点击链接扫码关注【RecBole-GNN/源码】RecBole-GNN中lightGCN源码解析 【RecBole-GNN/源码】RecBole-GNN中lightGCN源码解析 原文&…

【C++】初识CC++内存管理

前言 我们都知道C&C是非常注重性能的语言&#xff0c;因此对于C&C的内存管理是每一个C/C学习者必须重点掌握的内容&#xff0c;本章我们并不是深入讲解C&C内存管理&#xff0c;而是介绍C&C内存管理的基础知识&#xff0c;为我们以后深入理解C&C内存管理做铺…

基于 U-Net 网络的遥感图像语义分割 完整代码+论文

一、研究目的U-Net 是一种由全卷积神经网络启发的对称结构网络&#xff0c;在医疗影像分割领域取得了很好的效果。 此次研究尝试使用 U-Net 网络在对多光谱遥感影像数据集上进行训练&#xff0c;尝试使用卷积神经网络自动分割出建筑&#xff0c;希望能够得到一种自动分割遥感影…

ElementUI分页的实现

官网地址&#xff1a;Element - The worlds most popular Vue UI framework 第一步&#xff1a;拷贝你喜欢的分页类型放在你的组件页面需要用到的分页位置 <el-paginationsize-change"handleSizeChange"current-change"handleCurrentChange":current-p…

1.JAVA-JDK安装

前言&#xff1a;工具下载地址阿里云盘&#xff1a;Java-Jdk&#xff1a;https://www.aliyundrive.com/s/JpV55xhVq2A提取码: j53y一、jdk下载&#xff1a;前往Oracle官网可免费下载地址&#xff1a;https://www.oracle.com/java/technologies/downloads/ 此处我下载的是jdk8&a…

【nas折腾篇】抉择吧,是入门还是放弃

2018年公司一位女同事问群晖的nas是否值得买。我一脸懵&#xff0c;以前给公司买云服务有采购nas盘&#xff0c;直接mount挂到服务器上当存储&#xff0c;但对于单独的nas服务器没有什么概念。一晃几年过去了&#xff0c;陆续刷到些nas服务的视频&#xff0c;周边朋友用nas的也…

nginx的介绍及源码安装

文章目录前言一、nginx介绍二、nginx应用场合三、nginx的源码安装过程1.下载源码包2.安装依赖性-安装nginx-创建软连接-启动服务-关闭服务3.创建nginx服务启动脚本4.本实验---纯代码过程前言 高可用&#xff1a;高可用(High availability,缩写为 HA),是指系统无中断地执行其功…

OSI七层模型与物理层与设备链路层

目录 协议 举例 OSI七层模型 理解七层模型 以下为OSI七层模型数据逐层封装和数据逐层解封的过程 TCP/IP参考模型 数据包的层层封装与层层拆包 各层的数据以及协议 封装所用的协议的数字表示形式 物理层 模拟信号 模拟信号特点 数字信号 数字信号特点 数据通信模…

【存储】etcd的存储是如何实现的(3)-blotdb

前两篇分别介绍了etcd的存储模块以及mvcc模块。在存储模块中&#xff0c;提到了etcd kv存储backend是基于boltdb实现的&#xff0c;其在boltdb的基础上封装了读写事务&#xff0c;通过内存缓存批量将事务刷盘&#xff0c;提升整体的写入性能。botldb是etcd的真正的底层存储。本…