热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

计算几何算法2.关于线和点到线的距离(二维和三维)(人家写的,不能转发标记一下)

转发来自http:www.everyinch.netindex.phpcomputergeometry2关于直线直线方程点到直线的距离用两点表示的直线2d隐式表示的直线的情形参数方

转发来自 http://www.everyinch.net/index.php/computergeometry2/

 

关于直线
直线方程
点到直线的距离
用两点表示的直线
2d隐式表示的直线的情形
参数方程表示的直线
一个点到射线或线段的距离
代码实现

距离计算是计算机图形学和计算几何的基本问题,而且有很多关于这方面的公式。不过,由于对象描述方式不同,有替代方案可供选择。我们将说明其中的一些情况。

自始至终,我们需要有一个度量来计算两点之间的距离。我们假定有基于毕达哥拉斯定理的欧几里德度量标准。也就是说,对于一个n维向量v =(用V1,V2 ,…,Vn),其长度| v |由下式给出:

clip_image002

而两点P值(的p1,p2 ,…, pn)和Q =(q1,q2及,…, qn),它们之间的距离是:

clip_image004

关于直线:

定义直线L是通过给两个不同的点P0和 P1来定义的。事实上,它定义了一条从P0至P1的线段,这是古希腊人理解直线的方式,它与我们所知道的两个端点之间的最短路径的自然直觉相吻合。直线可以向两端无限地延长,这就是我们今天经常提到的直线的概念。不过,对于古希腊人,线是可以无限延长的有限线段。这种定义方式的好处是,它适用于所有维度:二维,三维,或任何n维空间。而且,一般在实际应用中通过端点来定义一条线段,因为线段一般用来表示多边形、多面体或内嵌图形的边。

直线L也可以通过一个点和一个方向来定义。让P0是直线L上的一点,vL是表示直线方向的非零向量。这个方法等价于直线的两点定义的方式,由于可以把vL=(P1-P0)。或者给定P0和vL,可以选择P1=P0+vL 作为直线的第二个点。如果vL被归一化为方向的单位向量,uL=vL/|vL|,那么它的元素就是直线L的方向余弦。也就是说,在n 维,让qi (i=1,n)是直线L和第i个轴ai的角度(例如,在2D,a1是在x轴和a2是y轴)。那么,向量vL = (vi),有vi = cos(qi)是直线L的方向向量。在2d如下图所示,如果q 是直线L与x轴的角度,那么cos(q2) = sin(q),即vL = (cos(q1), cos(q2)) = (cos(q), sin(q)) 是L的单位方向向量,因为:cos2(q) + sin2(q) = 1。同样,在任意维度上,方向余弦的平方和为1,即:S cos2(qi) = 1。

clip_image006

直线方程:

直线也可以定义为为未知方向上的点坐标方程。以下是一个在实践中通常遇到方程式:


类型方程式使用
2d显式的表示方法y = f(x) = mx+b非垂直的2d直线
2d隐式的表示方法f(x,y) = ax + by + c = 0任意2d直线
参数方法P(t) = P0 + tvL任意维度的任意直线

2d显式的表示方法是在中学讲过的,但它不是最灵活的方法。2d隐式的表示方法是较为有效的,显式方法转换为隐式方法是容易实现的。通常使用隐式方法的两个参数定义一个向量nL=(a,b),它垂直于直线L。这是因为直线L上任意两点P0=(x0,y0) 和 P1=(x1,y1),我们有nL·vL = (a,b)·(P1-P0) = a(x1–x0) + b(y1–y0) = f(P1) – f(P0) = 0。因此,我们说nL是直线L的单位向量,意味着垂直于直线L。而且,给定任意直线的法向量nL=(a,b)和一个直线上任意一点P0,隐式方程的法线形式是:

clip_image008

这个方程如果a2 + b2 = 1则是归一化的,从而nL是单位法向量,常常用uL表示。

不幸的是,一个隐式(或显式)方程仅仅能够定义一条2d直线,而在3d中直线方程定义了一个平面,在n维空间中它定义了一个n-1维的超平面。

另一方面,在任意n维空间,直线的参数方程是有效的,也是最通用的一种表示方法。通过两个点P0和P1、方向向量vL来定义一条直线,即:

clip_image010

其中t是一个实数。其中P(0)&#61;P0&#xff0c;P(1)&#61; P1。P(t) 有 0<t<1 表示P0和P1之间的线段&#xff0c;其中t是分数。也就是说&#xff0c;t &#61; d(P0,P(t)) / d(P0,P1)&#xff0c;因此&#xff0c;P(1/2)&#61;(P0&#43;P1)/2 是线段的中点。而且&#xff0c;如果t <0&#xff0c;则P(t)位于P0方向的外侧&#xff0c;如果t >1&#xff0c;则P(t)位于P1方向的外侧。

我们可以方便地从一种表示方法向另一种表示方法转换。例如&#xff0c;给定两个直线上的点P0&#61;(x0,y0) 和 P1&#61;(x1,y1) &#xff0c;可以推导出它的隐式方程&#xff0c;即通过vL&#61;(xv,yv)&#61;P1-P0&#61;(x1–x0, y1–y0)&#xff0c;我们有nL&#61;(-yv,xv)&#61;(y0–y1,x1–x0) 是垂直于直线L的法向量。因为 nL·vL&#61; 0&#xff0c;直线L的隐式方程为&#xff1a;

clip_image012

其中x和y的系数是nL的组成部分。

另外&#xff0c;在2d&#xff0c;如果直线L与x轴的角度为q&#xff0c;回忆一下即vL &#61; (cos(q), sin(q)) 是单位方向向量&#xff0c;那么nL &#61; (-sin(q), cos(q)) 是单位法向量。所以如果P0&#61;(x0,y0)是直线L上一点&#xff0c;那么直线L归一化的隐式方程为&#xff1a;

clip_image014

因为sin2(q) &#43; cos2(q) &#61; 1。参数方程是&#xff1a;

clip_image016

点到直线的距离&#xff1a;

给定一条线L和一个点P&#xff0c;让d(P,L) 表示点P到直线L的距离&#xff0c;这是P到直线L的最短距离&#xff0c;假如L是一条无限延伸的直线&#xff0c;那么这是一个从点P到直线L的垂线。但是如果直线L是一条有限长度的线段S&#xff0c;那么垂线的底可能超出线段&#xff0c;这是计算最短距离需要考虑一些其他的因素。我们首先考虑点到无限延伸的直线的垂直距离。

用两点表示的直线&#xff1a;

在2d和3d中&#xff0c;直线L是由两个点P0和 P1确定&#xff0c;可以使用叉乘直接计算从任意一点P到直线L的距离。2d情况可以作为z轴等于0的特例来处理。关键的发现是两个3d向量的叉乘等于由它们围起来的平行四边形的面积&#xff0c;既然|v×w| &#61; |v| |w| |sin| &#xff0c;其中q 是两个向量v和w之间的夹角。同时&#xff0c;平行四边形的面积还等于底乘以高&#xff0c;我们把高定义为d(P,L)。vL&#61;P0P1&#61;(P1-P0) 和w&#61;P0P&#61;(P-P0)&#xff0c;如图&#xff1a;

clip_image018

那么&#xff0c;|vL× w| &#61; Area(平行四边形(vL,w) ) &#61; |vL| d(P,L)的结果为&#xff1a;

clip_image020

其中uL&#61; vL / |vL| 是直线L的单位方向向量&#xff0c;如果需要计算许多个点到一条直线的距离&#xff0c;首先计算 uL是最有效的方式。

对于内嵌到3d空间的2d情况&#xff0c;点P&#61;(x,y,0)&#xff0c;叉乘为&#xff1a;

clip_image022

和距离计算公式为&#xff1a;

clip_image024

2d隐式表示的直线的情形&#xff1a;

在2d&#xff0c;直线L是定义为隐式方程f(x,y) &#61; ax&#43;by&#43;c &#61; 0。对于任意2d点P&#61;(x,y)&#xff0c;距离d(P,L)可以利用这个方程来直接计算。

回忆一下&#xff0c;向量nL &#61; (a,b)垂直于直线L。利用nL可以计算任意点P到直线L的距离。手续ian选择直线L上任意点P0&#xff0c;然后做P0P到nL的垂线&#xff0c;如下图所示&#xff1a;

clip_image026

我们有&#xff1a;
&#xff08;1&#xff09;由于a和b不能同时为零&#xff0c;假设一个a!&#61;0&#xff0c;并选择直线上点P0&#61;(-c/a,0)&#xff0c; [否则&#xff0c;若a &#61; 0而b !&#61; 0&#xff0c;选择P0&#61;(0,-c/b)&#xff0c;具有同样的结果]
&#xff08;2&#xff09;对于任意直线L上的点P0我们有&#xff1a;nL · P0P &#61; |nL| |P0P| cos q &#61; |nL| d(P,L)
&#xff08;3&#xff09;对于我们指定的P0&#xff1a; nL ·P0P &#61; (a,b· (x&#43;c/a, y) &#61; ax&#43;c&#43;by &#61; f(x,y) &#61; f(P)
结合&#xff08;2&#xff09;和&#xff08;3&#xff09;得出公式如下&#xff1a;

clip_image028

而且&#xff0c;我们可以在|nL|上除以f(x,y)的系数而预先归一化方程&#xff0c;使 |nL|&#61;1。就会得到非常有效率的公式&#xff1a;

clip_image030

其中对于每次的距离计算只有2次乘法和2次加法。因此&#xff0c;如果需要计算二维多点到直线L的距离&#xff0c;那么应该计算单位法向量来使用这个公式。另外请注意&#xff0c;如果只是比较距离&#xff08;例如&#xff0c;需要寻找距离直线最近或最远的点&#xff09;&#xff0c;那么归一化是没有必要&#xff0c;因为它只是改变了一个常数因子。

回忆以下&#xff0c;有直线L&#xff0c;做直线L与x轴的夹角和直线上任意点 P0&#61;(x0,y0)&#xff0c;然后归一化的隐式方程有&#xff1a;a &#61; -sin(q), b &#61; cos(q), c &#61; sin(q)x0 -cos(q)y0.

参数方程表示的直线&#xff1a;

为了计算任意维度空间从任意点P到直线L的距离d(P,L)&#xff0c;直线L的方程由参数方程给出。假设P(b)为点P到直线L的垂足。那么直线有参数方程给出&#xff1a;P(t)&#61;P0 &#43; t (P1-P0)。那么向量P0P(b)是向量P0P在线段P0P1上的投影&#xff0c;如图所示&#xff1a;

clip_image032

因此&#xff0c;通过vL&#61;(P1-P0)和w&#61;(P-P0)&#xff0c;有&#xff1a;

clip_image034

clip_image036

这里uL是直线的单位方向向量。这个公式的优势是&#xff1a;可以工作在任意维度上&#xff0c;有时候计算垂足P(b)也是有用的。在3D中叉乘公式是更有效率的。在2D当不需要P(b)时&#xff0c;隐式的公式更好&#xff0c;特别是计算多点到同一直线的距离的时候。

一个点到射线或线段的距离

射线R是从点P0开始向某一方向无限延伸。它可以使用参数方程表示为P(t)&#xff0c;其中t>&#61;0有起点P0。线段S有两个端点P0和P1&#xff0c;使用参数方程表示为P(0)&#61;P0 和P(1)&#61;P1 &#xff0c;P(t)有 0<&#61;t<&#61;1。

计算任意点P到射线R和线段S的距离和计算任意点P到直线L的距离之间的差异在于&#xff1a;从点P到直线L的垂足P(b)有可能位于射线R或线段S之外&#xff0c;在这种情况下&#xff0c;实际的最短距离是点P到射线R的起点P0或是到线段S的两个端点之一的距离。

clip_image038

对于射线只有一种选择。但对于线段S而言必须确定哪个端点距离点P最近&#xff0c;我们可以计算两个端点到点P的距离&#xff0c;然后使用最短的那个&#xff0c;但这不是最有效率的办法。我们首先确定点P的垂足位于线段S之外&#xff0c;一个容易的做法是考虑线段P0P或P1P和线段P0P1之间的角度。如果任意角度是90度&#xff0c;那么P(b)是对应的端点。如果不是90度&#xff0c;那么看它们是锐角还是钝角。可以通过点积的方法来进行测试。这个结果决定应该使用哪个端点来计算距离&#xff0c;或者计算点P到线段S的垂直距离本身。它可以工作在n为空间&#xff1a;

clip_image040

既然w&#61; v &#43; w1&#xff0c;两次测试的结果w0·vv·v实际是距离计算公式的分子和分母&#xff0c;通过预先保留计算结果从而提高算法的效率&#xff1a;


1distance( Point P,  Segment P0:P1 )

2{

3       v &#61; P1 - P0

4       w &#61; P - P0

5 

6       if ( (c1 &#61; w•v) <&#61; 0 )  // before P0

7               return d(P, P0)

8       if ( (c2 &#61; v•v) <&#61; c1 ) // after P1

9               return d(P, P1)

10 

11       b &#61; c1 / c2

12       Pb &#61; P0 &#43; bv

13       return d(P, Pb)

14}

然而&#xff0c;在二维情况下&#xff0c;如果我们需要计算从多点到射线或线段的距离&#xff0c;使用规范化方程式做是否一点P有一个新的最小距离的初步测试从L会更加有效&#xff1b;在实践中&#xff0c;一个特定的应用细节将决定哪些是最有效的方法。

代码实现


1// Copyright 2001, softSurfer (www.softsurfer.com)

2// This code may be freely used and modified for any purpose

3// providing that this copyright notice is included with it.

4// SoftSurfer makes no warranty for this code, and cannot be held

5// liable for any real or imagined damage resulting from its use.

6// Users of this code must verify correctness for their application.

7 

8// Assume that classes are already given for the objects:

9//    Point and Vector with

10//        coordinates {float x, y, z;} (z&#61;0 for 2D)

11//        appropriate operators for:

12//            Point  &#61; Point ± Vector

13//            Vector &#61; Point - Point

14//            Vector &#61; Scalar * Vector

15//    Line with defining endpoints {Point P0, P1;}

16//    Segment with defining endpoints {Point P0, P1;}

17//&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;

18// dot product (3D) which allows vector operations in arguments

19#define dot(u,v)   ((u).x * (v).x &#43; (u).y * (v).y &#43; (u).z * (v).z)

20#define norm(v)    sqrt(dot(v,v))  // norm &#61; length of vector

21#define d(u,v)     norm(u-v)       // distance &#61; norm of difference

22 

23// closest2D_Point_to_Line(): finds the closest 2D Point to a Line

24//    Input:  an array P[] of n points, and a Line L

25//    Return: the index i of the Point P[i] closest to L

26int

27closest2D_Point_to_Line( Point P[], int n, Line L)

28{

29    // Get coefficients of the implicit line equation.

30    // Do NOT normalize since scaling by a constant

31    // is irrelevant for just comparing distances.

32    float a &#61; L.P0.y - L.P1.y;

33    float b &#61; L.P1.x - L.P0.x;

34    float c &#61; L.P0.x * L.P1.y - L.P1.x * L.P0.y;

35 

36    // initialize min index and distance to P[0]

37    int mi &#61; 0;

38    float min &#61; a * P[0].x &#43; b * P[0].y &#43; c;

39    if (min <0) min &#61; -min;    // absolute value

40 

41    // loop through Point array testing for min distance to L

42    for (i&#61;1; i

43        // just use dist squared (sqrt not needed for comparison)

44        float dist &#61; a * P[i].x &#43; b * P[i].y &#43; c;

45        if (dist <0) dist &#61; -dist;   // absolute value

46        if (dist // this point is closer

47            mi &#61; i;          // so have a new minimum

48            min &#61; dist;

49        }

50    }

51    return mi;    // the index of the closest Point P[mi]

52}

53//&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;

54//dist_Point_to_Line(): get the distance of a point to a line.

55//    Input:  a Point P and a Line L (in any dimension)

56//    Return: the shortest distance from P to L

57float

58dist_Point_to_Line( Point P, Line L)

59{

60    Vector v &#61; L.P1 - L.P0;

61    Vector w &#61; P - L.P0;

62 

63    double c1 &#61; dot(w,v);

64    double c2 &#61; dot(v,v);

65    double b &#61; c1 / c2;

66 

67    Point Pb &#61; L.P0 &#43; b * v;

68    return d(P, Pb);

69}

70//&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;

71//dist_Point_to_Segment(): get the distance of a point to a segment.

72//    Input:  a Point P and a Segment S (in any dimension)

73//    Return: the shortest distance from P to S

74float

75dist_Point_to_Segment( Point P, Segment S)

76{

77    Vector v &#61; S.P1 - S.P0;

78    Vector w &#61; P - S.P0;

79 

80    double c1 &#61; dot(w,v);

81    if ( c1 <&#61; 0 )

82        return d(P, S.P0);

83 

84    double c2 &#61; dot(v,v);

85    if ( c2 <&#61; c1 )

86        return d(P, S.P1);

87 

88    double b &#61; c1 / c2;

89    Point Pb &#61; S.P0 &#43; b * v;

90    return d(P, Pb);

91}

92//&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;

参考文献

David Eberly, “Distance Between Point and Line, Ray, or Line Segment”, Magic Software
Euclid, The Elements, Alexandria (300 BC)
Andrew Hanson, “Geometry for N-Dimensional Graphics” in Graphics Gems IV (1994)
Thomas Heath, The Thirteen Books of Euclid’s Elements, Vol 1 (Books I and II) (1956)
Jack Morrison, “The Distance from a Point to a Line”, in Graphics Gems II (1994)


推荐阅读
  • [c++基础]STL
    cppfig15_10.cppincludeincludeusingnamespacestd;templatevoidprintVector(constvector&integer ... [详细]
  • poj 3352 Road Construction ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 思科IOS XE与ISE集成实现TACACS认证配置
    本文详细介绍了如何在思科IOS XE设备上配置TACACS认证,并通过ISE(Identity Services Engine)进行用户管理和授权。配置包括网络拓扑、设备设置和ISE端的具体步骤。 ... [详细]
  • php更新数据库字段的函数是,php更新数据库字段的函数是 ... [详细]
  • 本文详细介绍了MySQL数据库的基础语法与核心操作,涵盖从基础概念到具体应用的多个方面。首先,文章从基础知识入手,逐步深入到创建和修改数据表的操作。接着,详细讲解了如何进行数据的插入、更新与删除。在查询部分,不仅介绍了DISTINCT和LIMIT的使用方法,还探讨了排序、过滤和通配符的应用。此外,文章还涵盖了计算字段以及多种函数的使用,包括文本处理、日期和时间处理及数值处理等。通过这些内容,读者可以全面掌握MySQL数据库的核心操作技巧。 ... [详细]
  • Codeforces竞赛解析:Educational Round 84(Div. 2评级),题目A:奇数和问题
    Codeforces竞赛解析:Educational Round 84(Div. 2评级),题目A:奇数和问题 ... [详细]
  • OpenAI首席执行官Sam Altman展望:人工智能的未来发展方向与挑战
    OpenAI首席执行官Sam Altman展望:人工智能的未来发展方向与挑战 ... [详细]
  • Android 构建基础流程详解
    Android 构建基础流程详解 ... [详细]
  • Python 伦理黑客技术:深入探讨后门攻击(第三部分)
    在《Python 伦理黑客技术:深入探讨后门攻击(第三部分)》中,作者详细分析了后门攻击中的Socket问题。由于TCP协议基于流,难以确定消息批次的结束点,这给后门攻击的实现带来了挑战。为了解决这一问题,文章提出了一系列有效的技术方案,包括使用特定的分隔符和长度前缀,以确保数据包的准确传输和解析。这些方法不仅提高了攻击的隐蔽性和可靠性,还为安全研究人员提供了宝贵的参考。 ... [详细]
  • 在PHP中,高效地分割字符串是一项常见的需求。本文探讨了多种技术,用于在特定字符(如“或”)后进行字符串分割。通过使用正则表达式和内置函数,可以实现更加灵活和高效的字符串处理。例如,可以使用 `preg_split` 函数来实现这一目标,该函数允许指定复杂的分隔符模式,从而提高代码的可读性和性能。此外,文章还介绍了如何优化分割操作以减少内存消耗和提高执行速度。 ... [详细]
  • 当使用 `new` 表达式(即通过 `new` 动态创建对象)时,会发生两件事:首先,内存被分配用于存储新对象;其次,该对象的构造函数被调用以初始化对象。为了确保资源管理的一致性和避免内存泄漏,建议在使用 `new` 和 `delete` 时保持形式一致。例如,如果使用 `new[]` 分配数组,则应使用 `delete[]` 来释放内存;同样,如果使用 `new` 分配单个对象,则应使用 `delete` 来释放内存。这种一致性有助于防止常见的编程错误,提高代码的健壮性和可维护性。 ... [详细]
  • MyISAM和InnoDB是MySQL中最为广泛使用的两种存储引擎,每种引擎都有其独特的优势和适用场景。MyISAM引擎以其简单的结构和高效的读取速度著称,适用于以读操作为主、对事务支持要求不高的应用。而InnoDB引擎则以其强大的事务处理能力和行级锁定机制,在需要高并发写操作和数据完整性的场景下表现出色。选择合适的存储引擎应综合考虑业务需求、性能要求和数据一致性等因素。 ... [详细]
  • 尽管我们尽最大努力,任何软件开发过程中都难免会出现缺陷。为了更有效地提升对支持部门的协助与支撑,本文探讨了多种策略和最佳实践,旨在通过改进沟通、增强培训和支持流程来减少这些缺陷的影响,并提高整体服务质量和客户满意度。 ... [详细]
  • 如何将Python与Excel高效结合:常用操作技巧解析
    本文深入探讨了如何将Python与Excel高效结合,涵盖了一系列实用的操作技巧。文章内容详尽,步骤清晰,注重细节处理,旨在帮助读者掌握Python与Excel之间的无缝对接方法,提升数据处理效率。 ... [详细]
author-avatar
张佩君宣真威任
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有