zoukankan      html  css  js  c++  java
  • 3维图形概述

    WPF 3-D功能使得开发员能够利用XAML和源代码绘制,转换,和3-D图形的动画效果。开发员能够结合2-D和3-D图形去创造更丰富的控件,提供复杂的数据说明,或者提高应用程序界面的用户体验。WPF支持的3D设计的目的不是为了提供一个全功能的游戏开发平台。这个主题提供了WPF图形系统3D功能的概述。

    这个主题包含如下的内容。

    3-D in a 2-D Container

    WPF 3-D图形的内容被封装进了一个元素:Viewport3D,能够参与2元素的结构。图形系统将Viewport3D处理为2维可视化元素,与其他的WPF元素一样。Viewport3D函数作为一个窗口-一个视口-变成了3维的场景。更准确的说,这是一个被3-D场景投影的表面。

    在常规的2-D应用中,使用Viewport3D作为你的另外一个容器,就像Grid和Canvas一样。尽管你可以在Viewport3D里使用其他的2-D绘图对象,你不能在一个Viewport3D里相互渗透2-D和3-D对象。这个话题将集中在Viewport3D的3-D图形。

    3-D Coordinate Space

    WPF坐标系统的原点在呈现区域(屏幕)的左上角。在二维系统里, x轴的正方向为右手方向,y轴的正方向向下。在3维坐标系统里,然而,原点位于呈现区域的中间,x轴的正方向为右边,但是y轴的正方向向上,z轴的正方向是原点向外,指向观察者。

    常规的二维和三维坐标系统图:

    在WPF里,通过坐标轴定义的空间是3-D对象静止的参照系。当你在这个空间里创造模型和创造灯光和相机来查看它们时,区别静态的参照系是非常有意义的,或者“世界坐标”。当你从局部的参照系创建每个模型时,你需要进行转换。记住对象在世界坐标系看起来完全不一样,或根本就不可见,依赖于灯光和照相机设置,照相机的位置并没有改变世界坐标对象的位置。

    在2-D上开发的程序员习惯了在二维的屏幕上进行定位绘制。当你创建了一个3-D的场景,重要的是必须记住你是创建了2-D来表示3-D对象。因为3-D的场景依赖于观察者观察点,所以你必须规定观察点。Camara类允许你去3-D场景的观察点。

    另外一种方法理解在2-D的表面呈现3-D的场景,是通过将场景投影到观察面。ProjectionCamera允许你指定不同的投影和他们的属性去改变3-D模型的外观。Perspectivecamera指定了投影场景透明度,换句话说,Perspective提供了一个消逝点的透视。你可以指定场景里坐标系空间照相机的位置,以及照相机的方向和视角,和一个定义了场景上方的矢量。如下的图表说明了PerspectiveCamara的投影。

    ProjectionCamera的NearPlaneDistance和FarPlaneDistance属性限制了照相机的投影范围。因为照相机可以定位到场景的任何地方。照相机实际上可能在模型的内部或者附近定位,使它很难区别对象属性。NearPlaneDistance允许你规定照相机到对象的最小的距离。反过来手,FarPlaneDistance使你规定了对象和照相机最远的距离,以确保对象不会太远以至于场景不能识别它。

    照相机的位置

    OrthographicCamera规定了3-D和2-D可视表面的垂直投影。像别的照相机一样,它规定了一个位置,观察方向和“向上”方向。不像PerspectiveCamera。然而,OrthographicCamera描述的投影并没有包括透视。换句话说,OrthographicCamera描述了景色盒子,它们的边是平行的,而不是两边相交于一点的相机。如下的图像显示了利用PerspectiveCamera和orthographicCamera观察的相同的模型。

    如下的代码是典型的相机设置。

    PerspectiveCamera myPCamera = new PerspectiveCamera();

    myPCamera.Position = new Point3D (0,0,2)

    myPCamera.LookDirection = new Vector3D(0, 0, -1)

    myPCamera.FieldOfView = 60;

    myViewport3D.Camera = myPCamera;

    Model and Mesh Primitives

    Model3D是一个抽象的基类来表示一个通用的3-D对象。为了建立一个3-D场景,你需要察看一些对象,这些继承自Model3D的对象组成了场景。目前,WPF支持的模型几何体是GeometryModel3D。这个模型的Geometry属性进行网格原型。

    为了建立一个模型,从一个基本的或者网格开始建立。一个3-D基本图是单个3-D实体的顶点集。大部分3-D系统提供了简单封闭图形作为模型蓝本:通过三个顶点定义的三角形。因为三角形的三个顶点共面,你可以继续增加三角形来模拟更加复杂的形状,称为网格。

    WPF的3-D系统目前提供了MeshGeometry3D类,允许你规定任意几何。它目前不支持预定义的基本3-D图形,例如球体和立方体形式。通过指定一系列三角形顶点作为位置属性开始创建MeshGeomety3D。每个顶点指定为Point3D。(在标记应用扩展语言(XAML),规定这个属性作为一个三个顶点坐标组成的列表),根据它的几何形状,你的网格可能由许多三角形组成,有一些共用顶点坐标。为了正确的绘制网格,WPF需要哪些三角形共用了顶点的信息。通过指定一组具有TriangleIndices属性的列表来提供信息。这个列表指定了哪些点的位置列表将决定三角形。

    <GeometryModel3D>

    <GeometryModel3D.Geometry>

    <MeshGeometry3D

      Positions = “-1 -1 0  1 -1 0  -1 1 0  1 1 0”

      Normals = “0 0 1  0 0 1  0 0 1  0 0 1”

      TextureCoordinates= ”0 1  1 1  0 0  1 0”

      TriangleIndices = “0 1 2  1 3 2”

    <GeometryModel3D.Geometry>

    <GeometryModel3D.Material>

                    <DiffuseMaterial>

                                    <DiffuseMaterial.Brush>

                                                    <SolidColorBrush Color=”Cyan” Opacity=”0.3” />

                                    <DiffuseMaterial.Brush>

    </DiffuseMaterial>

    </GeometryModel3D.Material>

    <GeometryModel3D.Transform>

                    <TranslateTransform3D

       OffsetX=”2” OffsetY=”0”  OffsetZ=”-1” >

    </TranslateTransform3D>

    <GeometryModel3D.Transform>

    </GeometryModel3D>

    在上面的例子中,Positions列表中指定了8个顶点来定义立方体网格。TriangleIndices属性指定了12个3个索引组。列表中的每个数字在Positions列表里引用了偏移量。例如,这里有三个顶点通过Positions进行指定:(1,1,0), (0,1,0), (0,0,0)。首先,这三个索引通过TriangleIndices指定为0, 2, 1,与Positions列表中的第一个,第三个,第二点对应。结果是,第一个三角形组成的立方模型将由(1,1,0) (0,1,0) 和(0,0,0)组成。剩下的11个三角形将使用相似的方法决定。

    你可以通过指定Normals和TextureCoordinates的值连续定义模型。为了呈现模型的表面,图形系统需要信息,关于面对任意给出三角形表面的方向信息。使用这些信息来明白模型的计算:面朝光源的表面看起来比其他远离灯光角度的大。尽管WPF可以通过使用位置坐标来决定默认法向量,你也可以指定不同的法向量来近似曲面的外观。

    TextureCoordinates属性指定了一个点集告诉图形系统怎样映射坐标,来决定如何将纹理绘制到网格的定点。TextureCoordinates指定为从0到1。与Normals属性一样,图形系统可以计算默认的纹理坐标,但是你也可以选择设置不同的纹理坐标来控制重复模式纹理的映射,例如,纹理坐标的更多信息可以在随后的话题里找到,或者参考Direct3D SDK。

    如下的例子显示了如何在源代码中创建立方体模型的表面。注意到你可以将整个的立方体绘制为单个的GeometryModel3D;这个例子作为独特的模型绘制了立方体表面,将应用到每个面分离的纹理。

    MeshGeometry3D  side1Plane = new MeshGeometry3D();

    side1Plane.Positions.Add(new Point3D(-0.5, -0.5, -0.5));

    side1Plane.Positions.Add(new Point3D(-0.5, 0.5, -0.5));

    side1Plane.Positions.Add(new Point3D(0.5, 0.5, -0.5));

    side1Plane.Positions.Add(new Point3D(0.5, 0.5, -0.5));

    side1Plane.Positions.Add(new Point3D(0.5, -0.5, -0.5));

    side1Plane.Positions.Add(new Point3D(-0.5, -0.5, -0.5));

    side1Plane.TriangleIndices.Add(0);

    side1Plane.TriangleIndices.Add(1);

    side1Plane.TriangleIndices.Add(2);

    side1Plane.TriangleIndices.Add(3);

    side1Plane.TriangleIndices.Add(4);

    side1Plane.TriangleIndices.Add(5);

    side1Plane.Normals.Add(new Vector3D(0, 0, -1));

    side1Plane.Normals.Add(new Vector3D(0, 0, -1));

    side1Plane.Normals.Add(new Vector3D(0, 0, -1));

    side1Plane.Normals.Add(new Vector3D(0, 0, -1));

    side1Plane.Normals.Add(new Vector3D(0, 0, -1));

    side1Plane.Normals.Add(new Vector3D(0, 0, -1));

    side1Plane.TextureCoordinates.Add(new Point(1, 0));

    side1Plane.TextureCoordinates.Add(new Point(1, 1));

    side1Plane.TextureCoordinates.Add(new Point(0, 1));

    side1Plane.TextureCoordinates.Add(new Point(0, 1));

    side1Plane.TextureCoordinates.Add(new Point(0, 0));

    side1Plane.TextureCoordinates.Add(new Point(1, 0));

    Applying Materials to the Model

    一个网格看起来像一个三维对象,它必须有纹理来覆盖由点和三角形定义的表面,所以它可以通过照相机投影和点亮。在2-D,你使用Brush类来应用颜色,模式,梯度和其他在屏幕上的可视化内容。3-D对象的表面,然而,是一个发光模型的函数,不仅仅是将颜色和模式应用到它们。真实世界的对象根据表面质量的不同反射不同的光:光泽和光泽表面与粗糙和磨砂表面看起来不一样,并且有些对象吸收光,有些对象发光。你可以为3-D对象应用与2-D上相同的画刷,但是你不能直接应用它们。

    为了定义模型表面的特征,WPF使用了Material抽象类。材料具体的子类决定了一些模型表面的外观特征,并且同样提供了画刷属性,,是你可以传递SolidColorBrush, TitleBrush, VisualBrush.。

    DiffuseMaterial规定了画刷将被应用到点亮时漫射的模型。使用DiffuseMaterial大部分类似于使用2-D 模型的画刷;模型表面尽管有光泽,但没有反射光。

    SpecularMaterial规定了画刷将被应用到模型,尽管模型表面很硬或者光滑。具有反射强光的能力。你可以设置纹理的程度来建议反射的质量或者“发光”,通过指定SpecularPower属性的值。

    EmissiveMaterial允许你规定纹理,将应用到具有发出画刷颜色光能力的模型。这并没有使模型变亮;然而,如果纹理是Diffusematerial或者SpecularMaterial,它会在阴影里加入不同的东西。

    为了更好的性能,GeometryModel3D的背面(这些面不在视觉范围内,因为它们与照相机是相反的)可以从场景里抹掉。指定模型背面的材料,例如飞机,设置模型的BackMaterial属性

    为了获得表面特质,例如发光或者反光效果,你可以在一个模型里面交替使用几种不同的画刷。你可以通过MaterialGroup类应用和反复使用多种材料。MaterialGroup的孩子们被应用到从头到尾多个渲染通道。

    如下的代码显示了怎样将固体颜色和画刷应用到3-D模型。

    <GeometryModel3D.Material>

                    <DiffuseMaterial>

                                    <DiffuseMaterial.Brush>

                                                    <SolidColorBrush Color=”Cyan” Opacity=”0.3” />

                                    </DiffuseMaterial.Brush>

                    </DiffuseMaterial>

    </GeometryModel3D.Material>

    <DrawingBrush x:Key="patternBrush" Viewport="0,0,0.1,0.1" TileMode="Tile">
      <DrawingBrush.Drawing>
        <DrawingGroup>
          <DrawingGroup.Children>
            <GeometryDrawing Geometry="M0,0.1 L0.1,0 1,0.9, 0.9,1z"
              Brush="Gray" />
            <GeometryDrawing Geometry="M0.9,0 L1,0.1 0.1,1 0,0.9z"
              Brush="Gray" />
            <GeometryDrawing Geometry="M0.25,0.25 L0.5,0.125 0.75,0.25 0.5,0.5z"
              Brush="#FFFF00" />
            <GeometryDrawing Geometry="M0.25,0.75 L0.5,0.875 0.75,0.75 0.5,0.5z"
              Brush="Black" />
            <GeometryDrawing Geometry="M0.25,0.75 L0.125,0.5 0.25,0.25 0.5,0.5z"
              Brush="#FF0000" />
            <GeometryDrawing Geometry="M0.75,0.25 L0.875,0.5 0.75,0.75 0.5,0.5z"
              Brush="MediumBlue" />
          </DrawingGroup.Children>
        </DrawingGroup>
      </DrawingBrush.Drawing>
    </DrawingBrush>
    DiffuseMaterial side5Material = new DiffuseMaterial((Brush)Application.Current.Resources["patternBrush"]);

     Illuminating the Scene

    3-D图像里的灯光与真实世界里的灯关效果是一样的:使表面可见。更重要的一点,灯光确定了场景的哪一部分将被纳入投影。WPF里Light对象创建了各种灯光和阴影效果,仿照各种真实世界的灯光行为。你必须在你的场景里包含一种灯光,或者模型将是不可见的。

    如下的灯光继承自Light类:

    AmbientLight:提供环境光均匀的照亮所有的对象,无论其位置与方向。

    DirectionalLight: 像一个远距离的官员照明一样。定向灯光使用Vector3D来指定灯光方向,但是没有指定的位置。

    PointLight: 就像一个附近的光源点亮。PointLight有一个位置,并且从那个位置投射灯管,场景里的对象根据它们的位置和相对光源的距离进行点亮。PointLightBase暴露了一个Range属性,决定了超过了一定距离后将不会被照亮。PointLight同样暴露了衰减属性,决定了灯光随着距离而减少的强度。你可以指定为常数,线性的,或者二次插值灯光衰减。

    SpotLight: 继承自PointLight。聚光灯就像点灯一样照亮,并且有位置和方向。投影的光在一个锥形区里,通过InnerConeAngle和OuterConeAngle属性来设置角度。

    Lights是Model3D对象,所以你可以转换和动画光的属性,包含位置,颜色方向以及范围。

    <ModelVisual3D.Content>

                    <AmbientLight Color=”#333333”  />

    </ModelVisual3D.Content>

    DirectionalLight myDirLight = new DirectionalLight();

    myDirLight.Color = Colors.White;

    myDirLightColor = new Vector3D(-3, -4, -5);

    modelGroup.Children.Add(myDirLight);

    Transforming Models

    当你创建模型时,它们在场景里有一个特殊的地方。为了在场景周围移动这些模型,去旋转它们,或者改变它们的尺寸,改变顶点去定义模型本身是不实际的。相反的,就像在2-D模型里面一样,你对模型应用转换。

    每个模型对象都有一个 Transform属性,你可以移动,重定向或者重新定义模型的大小。当你应用了转换,你有效的偏移了所有的模型里所有点,无论是通过指定向量还是值进行转变。换句话说,你已经转变了定义模型的坐标空间(“模型空间”),但是你并没有改变整个场景坐标系(世界坐标)里组成模型几何的值。

    Animating Models

    WPF的3D 实现了与2-D图形一样的参与相同时间和动画系统。换句话说,为了制作一个3-D动画场景,需要动画模型属性。可以对动画属性进行直接的初始化,但是它通常很容易进行动画转换,改变模型的外表和位置。因为转变可以应用到Model3DGroup对象和单个对象,它可以应用一组动画到Model3DGroup的子集或另一组子对象集的动画。你也可以通过你场景照明性能的动画属性实现多种视觉效果。最终,你可以通过相机的位置和视野,选择动画自身的投影。

    WPF里为了使对象有动画效果,你需要创建一个时间线(某些属性沿着时间而改变),并将这些属性应用到动画中。因为3-D场景里所有的对象都继承于Viewport3D,你想应用动画场景的属性目标就是Viewport3D属性。

    假设你想做一个模型在一个地方进行摆动。你可能选择在模型里应用RotateTranform3D属性,并且它旋转的坐标轴是从一个矢量到另外一个。如下的代码示例演示了应用Vector3DAnimation到旋转Rotation3D的轴属性,假设RotateTransform3D是应用到TransformGroup模型中的一个。

    RotateTransform3D myRotateTransform = new RotateTransform3D(new AxisAngleRotation3D(new Vector(0, 1, 0), 1));

    Vector3DAnimation myVectorAnimation = new Vector3DAnimation(new Vector3D(-1, -1, -1), new Duration(TimeSpan.FromMilliseconds(5000)));

    myVectorAnimation.RepeatBehavior = Repeatbehavior.Forever;

    myRotateTransform.Rotation.BeginAnimation(AxisAngleRotation3D.AxisPropery, myVectorAnimation);

    cube1TransformGroup.Childern.Add(myRotateTransform);

    Add 3-D Content to the Window

    为了渲染这个场景,增加模型和光到Model3DGroup,然后设置Model3Dgroup为ModelVisual3D的内容,将ModelVisual3D增加到Viewport3D的子集。通过设置Camera 属性将照相机增加到Viewport3D。

    最终,增加Viewport3D到窗口。当Viewport3D被包含在布局元素里,例如Canvas,通过设置高度和宽度属性来设置Viewport3D的属性(继承至FrameworkElement)


    <UserControl x:Class="HostingWpfUserControlInWf.UserControl1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        >
     
        <Grid>
     
          <!-- Place a Label control at the top of the view. -->
          <Label 
                                       HorizontalAlignment="Center" 
                                       TextBlock.TextAlignment="Center" 
                                       FontSize="20" 
                                       Foreground="Red" 
                                       Content="Model: Cone"/>
     
          <!-- Viewport3D is the rendering surface. -->
          <Viewport3D Name="myViewport" >
     
            <!-- Add a camera. -->
            <Viewport3D.Camera>
              <PerspectiveCamera 
                                                        FarPlaneDistance="20" 
                                                        LookDirection="0,0,1" 
                                                        UpDirection="0,1,0" 
                                                        NearPlaneDistance="1" 
                                                        Position="0,0,-3" 
                                                        FieldOfView="45" />
            </Viewport3D.Camera>
     
            <!-- Add models. -->
            <Viewport3D.Children>
     
              <ModelVisual3D>
                <ModelVisual3D.Content>
     
                  <Model3DGroup >
                    <Model3DGroup.Children>
     
                      <!-- Lights, MeshGeometry3D and DiffuseMaterial objects are added to the ModelVisual3D. -->
                      <DirectionalLight Color="#FFFFFFFF" Direction="3,-4,5" />
     
                      <!-- Define a red cone. -->
                      <GeometryModel3D>
     
                        <GeometryModel3D.Geometry>
                          <MeshGeometry3D 
        Positions="0.293893 -0.5 0.404509  0.475528 -0.5 0.154509  0 0.5 0  0.475528 -0.5 0.154509  0 0.5 0  0 0.5 0  0.475528 -0.5 0.154509  0.475528 -0.5 -0.154509  0 0.5 0  0.475528 -0.5 -0.154509  0 0.5 0  0 0.5 0  0.475528 -0.5 -0.154509  0.293893 -0.5 -0.404509  0 0.5 0  0.293893 -0.5 -0.404509  0 0.5 0  0 0.5 0  0.293893 -0.5 -0.404509  0 -0.5 -0.5  0 0.5 0  0 -0.5 -0.5  0 0.5 0  0 0.5 0  0 -0.5 -0.5  -0.293893 -0.5 -0.404509  0 0.5 0  -0.293893 -0.5 -0.404509  0 0.5 0  0 0.5 0  -0.293893 -0.5 -0.404509  -0.475528 -0.5 -0.154509  0 0.5 0  -0.475528 -0.5 -0.154509  0 0.5 0  0 0.5 0  -0.475528 -0.5 -0.154509  -0.475528 -0.5 0.154509  0 0.5 0  -0.475528 -0.5 0.154509  0 0.5 0  0 0.5 0  -0.475528 -0.5 0.154509  -0.293892 -0.5 0.404509  0 0.5 0  -0.293892 -0.5 0.404509  0 0.5 0  0 0.5 0  -0.293892 -0.5 0.404509  0 -0.5 0.5  0 0.5 0  0 -0.5 0.5  0 0.5 0  0 0.5 0  0 -0.5 0.5  0.293893 -0.5 0.404509  0 0.5 0  0.293893 -0.5 0.404509  0 0.5 0  0 0.5 0  " 
        Normals="0.7236065,0.4472139,0.5257313  0.2763934,0.4472138,0.8506507  0.5308242,0.4294462,0.7306172  0.2763934,0.4472138,0.8506507  0,0.4294458,0.9030925  0.5308242,0.4294462,0.7306172  0.2763934,0.4472138,0.8506507  -0.2763934,0.4472138,0.8506507  0,0.4294458,0.9030925  -0.2763934,0.4472138,0.8506507  -0.5308242,0.4294462,0.7306172  0,0.4294458,0.9030925  -0.2763934,0.4472138,0.8506507  -0.7236065,0.4472139,0.5257313  -0.5308242,0.4294462,0.7306172  -0.7236065,0.4472139,0.5257313  -0.858892,0.429446,0.279071  -0.5308242,0.4294462,0.7306172  -0.7236065,0.4472139,0.5257313  -0.8944269,0.4472139,0  -0.858892,0.429446,0.279071  -0.8944269,0.4472139,0  -0.858892,0.429446,-0.279071  -0.858892,0.429446,0.279071  -0.8944269,0.4472139,0  -0.7236065,0.4472139,-0.5257313  -0.858892,0.429446,-0.279071  -0.7236065,0.4472139,-0.5257313  -0.5308242,0.4294462,-0.7306172  -0.858892,0.429446,-0.279071  -0.7236065,0.4472139,-0.5257313  -0.2763934,0.4472138,-0.8506507  -0.5308242,0.4294462,-0.7306172  -0.2763934,0.4472138,-0.8506507  0,0.4294458,-0.9030925  -0.5308242,0.4294462,-0.7306172  -0.2763934,0.4472138,-0.8506507  0.2763934,0.4472138,-0.8506507  0,0.4294458,-0.9030925  0.2763934,0.4472138,-0.8506507  0.5308249,0.4294459,-0.7306169  0,0.4294458,-0.9030925  0.2763934,0.4472138,-0.8506507  0.7236068,0.4472141,-0.5257306  0.5308249,0.4294459,-0.7306169  0.7236068,0.4472141,-0.5257306  0.8588922,0.4294461,-0.27907  0.5308249,0.4294459,-0.7306169  0.7236068,0.4472141,-0.5257306  0.8944269,0.4472139,0  0.8588922,0.4294461,-0.27907  0.8944269,0.4472139,0  0.858892,0.429446,0.279071  0.8588922,0.4294461,-0.27907  0.8944269,0.4472139,0  0.7236065,0.4472139,0.5257313  0.858892,0.429446,0.279071  0.7236065,0.4472139,0.5257313  0.5308242,0.4294462,0.7306172  0.858892,0.429446,0.279071  "                   TriangleIndices="0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 " />
                        </GeometryModel3D.Geometry>
     
                        <GeometryModel3D.Material>
                          <DiffuseMaterial>
                            <DiffuseMaterial.Brush>
                              <SolidColorBrush 
                                Color="Red" 
                                Opacity="1.0"/>
                            </DiffuseMaterial.Brush>
                          </DiffuseMaterial>
                        </GeometryModel3D.Material>
     
                      </GeometryModel3D>
     
                    </Model3DGroup.Children>
                  </Model3DGroup>
     
                </ModelVisual3D.Content>
     
              </ModelVisual3D>
     
            </Viewport3D.Children>
     
          </Viewport3D>
        </Grid>
     
    </UserControl>
     

     引用:http://msdn.microsoft.com/en-us/library/ms747437(VS.100).aspx

  • 相关阅读:
    FileReader:读取本地图片文件并显示
    uploadfy插件结合php案例
    php 生成二维码,图片上传到又拍云
    php get/post 请求(可用于请求api)获取手机号码归属地
    php中curl的详细解说
    聊聊Web App、Hybrid App与Native App的设计差异
    我的前端之路
    使用angular.js开发的一个简易todo demo
    在线个人简历(续)
    在线个人简历
  • 原文地址:https://www.cnblogs.com/longcloud/p/3159038.html
Copyright © 2011-2022 走看看