Direct2D教程V——位图(Bitmap)和位图笔刷(BitmapBrush)

简介: 目前博客园中成系列的Direct2D的教程有 1、万一的 Direct2D 系列,用的是Delphi 2009 2、zdd的 Direct2D 系列,用的是VS中的C++ 3、本文所在的 Direct2D教程 系列,用的是VS2010的Visual Basic语言(可以很方便的转为C#),基于Windows API Code Pack 1.1。

目前博客园中成系列的Direct2D的教程有

1、万一的 Direct2D 系列,用的是Delphi 2009

2、zdd的 Direct2D 系列,用的是VS中的C++

3、本文所在的 Direct2D教程 系列,用的是VS2010的Visual Basic语言(可以很方便的转为C#),基于Windows API Code Pack 1.1。

 

还有官方的说明文档 Direct2D ,用的是C++。

 

本系列的前几篇文章:

Direct2D教程I——简介及首个例子

Direct2D教程II——绘制基本图形和线型(StrokeStyle)的设置详解

Direct2D教程III——几何(Geometry)对象

Direct2D教程IV——笔刷(Brush)对象

 

Direct2D中的绘图函数

在Direct2D中,RenderTarget对象的绘图函数是DrawBitmap和DrawBitmapAtOrigin。这两个函数的原型定义如下:

 
Public  Sub DrawBitmap(bitmap As Direct2D1. D2DBitmap)
Public  Sub DrawBitmap(bitmap As Direct2D1. D2DBitmap, opacity As  Single, interpolationMode As Direct2D1. BitmapInterpolationMode)
Public  Sub DrawBitmap(bitmap As Direct2D1. D2DBitmap, opacity As  Single, interpolationMode As Direct2D1. BitmapInterpolationMode, destinationRectangle As Direct2D1. RectF)
Public  Sub DrawBitmap(bitmap As Direct2D1. D2DBitmap, opacity As  Single, interpolationMode As Direct2D1. BitmapInterpolationMode, destinationRectangle As Direct2D1. RectF, sourceRectangle As Direct2D1. RectF)
Public  Enum  BitmapInterpolationMode
    Linear = 1
    NearestNeighbor = 0
End  Enum

Public  Sub DrawBitmapAtOrigin(bitmap As Direct2D1. D2DBitmap, opacity As  Single, interpolationMode As Direct2D1. BitmapInterpolationMode, sourceRectangle As Direct2D1. RectF)

 

从函数的原型定义可以看出,DrawBitmap函数有4个重载函数,我们以最后一个函数说明各个参数的意义。

参数bitmap:要绘制的位图对象。参数类型是D2DBitmap类。

参数opacity:不透明度。范围是0-1,0表示全透明,1表示不透明

参数interpolationMode:缩放时的插值算法。参数类型是BitmapInterpolationMode枚举,分为Linear和NearestNeighbor

参数destinationRectangle:在RenderTarget上绘制图像的范围。参数类型是RectF类型。如果不指定的话,默认绘图在原点(左上角),大小是源位图的大小。

参数sourceRectangle:源位图需要绘制的范围。参数类型是RectF类型。不指定的话,指整个源位图。这个参数可以绘制源位图的一部分。

 

函数DrawBitmapAtOrigin指的是在原点位置绘制位图,参数意义和上面的一样,这里不再解释了(不过由于不牵涉到缩放操作,似乎参数interpolationMode没什么意义)

 

从上面的原型定义可以看出,在Direct2D中,有关位图的类是D2DBitmap类。有关位图的操作都和这个有关(绘制位图和位图笔刷(BitmapBrush))。但是,D2DBitmap类没有提供位图和文件转换的函数(从文件读取位图和把位图保存到文件中)。

 

在Direct2D中,有关位图文件的解码和编码(读取位图文件和保存位图文件)是WIC组件(和Direct2D命名空间平级的WindowsImagingComponent命名空间)

 

有关WIC(WindowsImagingComponent)组件的介绍如下:

Windows Imaging Component (WIC) 是 Windows Vista 中用于进行数字成像的扩展平台,它可以作为 .NET Framework 3.0 的部件或能够重新发布的独立组件,也用于 Windows XP 和 Windows Server 2003 中。WIC 在应用程序和 CODEC 之间提供一个抽象层,使应用程序不必专门认知特定的图像格式。无论哪种图像格式,只要机器上装有用于该图像格式的支持 WIC 的 CODEC,任何使用 Windows Imaging Component 的应用程序就能够通过一组一致的接口访问、显示、处理、保存和打印图像。

 

从上可以看出,WIC(WindowsImagingComponent)组件提供了Direct2D和文件系统的交流平台(Direct2D不必关心位图文件的编码和解码,WIC能自动的把各种格式的位图文件转换为Direct2D认可的位图格式)

 

 

 

利用WIC在Direct2D中绘制位图文件

 

由前面的内容可知,Direct2D中只能绘制D2DBitmap对象。

因此,利用WIC在Direct2D中绘制位图文件的核心内容就是把位图文件转换为D2DBitmap对象。它的操作过程如下:

1、创建WIC的ImagingFactory类。这个和Direct2D中的D2DFactory类类似。总管类,负责WIC的相关操作。很多的WIC中的类都得依靠它的相关函数才能创建

2、利用ImagingFactory类的CreateDecoderFromFileName函数,根据位图文件创建BitmapDecoder对象(实际上调用系统解码器解析位图文件)。

      BitmapDecoder对象有1个属性和1个函数

      FrameCount属性:只读属性,说明该位图对象包含的帧数。一般gif文件能包含多个帧,其余格式的一般只有1个帧

      GetFrame函数:返回指定帧对象。参数index是整形,说明是第几帧(从0开始)。返回的是BitmapFrameDecode对象

3、利用BitmapDecoder对象的GetFrame函数,返回指定帧的BitmapFrameDecode对象。(参数index一般是0,返回第1帧)

      BitmapFrameDecode对象只有1个函数。ToBitmapSource函数。将该对象转换为BitmapSource对象。

 

一般情况下,到此就可以了。但是,位图格式有很多,你可能不是很确定你的位图格式是否兼容Direct2D的D2DBitmap对象。因此,比较好的做法是继续下面的步骤,将位图格式转换为兼容Direct2D的D2DBitmap对象

 

4、利用ImagingFactory类的CreateFormatConverter函数创建FormatConverter对象。该对象负责进行格式转换。

5、调用FormatConverter对象的Initialize方法,进行格式转换。该对象还有一个ToBitmapSource函数。将转换好的位图对象转换为BitmapSource对象。

 

6、最后,利用RenderTarget对象的CreateBitmapFromWicBitmap函数将之前的BitmapSource对象转换为Direct2D的D2DBitmap对象

 

 

将Direct2D中的绘图内容保存到位图文件

前面提到如何在Direct2D中绘制位图文件。那么,反过来,将Direct2D中的绘图内容保存到位图文件也应该是可以的。

具体的操作过程如下:

1、利用RenderTarget对象的CreateBitmap函数创建D2DBitmap对象

2、利用D2DBitmap对象的CopyFromRenderTarget方法将RenderTarget上的内容复制到D2DBitmap对象中

3、利用WIC中的ImagingFactory类的CreateImagingBitmap函数创建ImagingBitmap对象

4、利用D2DFactory对象的CreateWicBitmapRenderTarget的函数创建基于ImagingBitmap对象的RenderTarget对象。我们命名为wicRenderTarget,以示区别。

5、调用wicRenderTarget对象的BeginDraw方法,准备绘图

6、调用wicRenderTarget对象的DrawBitmap方法,将步骤2中的D2DBitmap对象绘制到wicRenderTarget,实际上是绘制到对应的ImagingBitmap对象。

7、调用wicRenderTarget对象的EndDraw方法,结束绘图(也可以在步骤6中,利用绘图函数添加自己的水印)

8、根据要保存文件的后缀名,获得相对应的Guid对象

9、调用ImagingBitmap对象的SaveToFile方法,将ImagingBitmap中的内容保存到文件中。一共有三个参数:参数imagingFactory,类型是ImagingFactory;参数containerFormat,类型是Guid对象,指明文件的编码方式;参数fileName,要保存的文件名。

 

需要注意的是,上面的过程中,实际上有两个RenderTarget对象,一个是Direct2D中的RenderTarget和临时创建的wicRenderTarget对象。再利用wicRenderTarget对象的DrawBitmap方法把RenderTarget对象创建的D2DBitmap对象绘制到wicRenderTarget上。这里牵涉到资源共享的问题,在两个(或多个)RenderTarget对象之间共享D2DBitmap对象,则这两个RenderTarget对象创建时的RenderTargetProperties属性中的RenderTargetType的值必须相同(要么是Software,要么是Hardware)。但由于WIC只能是基于Software。因此,如果要用上面的步骤保存到位图文件,则必须把RenderTarget对象的RenderTargetType的值设为Software。但也因此降低了Direct2D的效率。

RenderTargetType的值有Software(用软件方式呈现绘图)、Hardware(用硬件方式呈现绘图)、Default(由系统判断采用软件还是硬件方式呈现绘图)

 

下面是读取位图文件和保存位图文件的示例代码:

 
Imports Microsoft.WindowsAPICodePack.DirectX
Imports WIC = Microsoft.WindowsAPICodePack.DirectX.WindowsImagingComponent

Public  Class  clsDirect2DSample10

    Protected _d2DFactory As Direct2D1. D2DFactory
    Protected _renderTarget As Direct2D1. RenderTarget
    Protected _renderProps As Direct2D1. RenderTargetProperties

    Protected _imagingFactory As WIC. ImagingFactory

    Public  Sub  New()
        _d2DFactory = Direct2D1. D2DFactory.CreateFactory()
        _imagingFactory = WIC. ImagingFactory.Create
    End  Sub

    Public  Sub CreateDeviceResource(Target As  Control)
        If _renderTarget Is  Nothing  Then

            _renderProps = New Direct2D1. RenderTargetProperties( _
                                                            Direct2D1. RenderTargetType.Software, _
                                                            New Direct2D1. PixelFormat( _
                                                                                 Graphics. Format.B8G8R8A8UNorm, _
                                                                                 Direct2D1. AlphaMode.Ignore), _
                                                            0, 0, Direct2D1. RenderTargetUsages.None, Direct3D. FeatureLevel.Default)

            _renderTarget = _d2DFactory.CreateHwndRenderTarget( _
                                                      _renderProps, _
                                                      New Direct2D1. HwndRenderTargetProperties( _
                                                                            Target.Handle, _
                                                                            New Direct2D1. SizeU(Target.Width, Target.Height), _
                                                                            Direct2D1. PresentOptions.None) _
                                                      )
        End  If

    End  Sub

    Public  Function LoadBitmapFromFile(fileName As  String, Optional frameIndex As  Integer = 0) As Direct2D1. D2DBitmap
        Dim decoder As WIC. BitmapDecoder = _imagingFactory.CreateDecoderFromFileName( _
                                                                                               fileName, _
                                                                                               WIC. DesiredAccess.Read, _
                                                                                               WIC. DecodeMetadataCacheOption.OnLoad)

        If frameIndex > decoder.FrameCount - 1 OrElse frameIndex < 0 Then frameIndex = 0
        Dim source As WIC. BitmapFrameDecode = decoder.GetFrame(frameIndex)

        Dim converter As WIC. FormatConverter = _imagingFactory.CreateFormatConverter()

        converter.Initialize(source.ToBitmapSource(), WIC. PixelFormats.Pbgra32Bpp, WIC. BitmapDitherType.None, WIC. BitmapPaletteType.MedianCut)

        Return _renderTarget.CreateBitmapFromWicBitmap(converter.ToBitmapSource())

    End  Function

    Public  Sub Render()
        If  Not _renderTarget Is  Nothing  Then

            With _renderTarget
                .BeginDraw()

                Dim B As Direct2D1. D2DBitmap = LoadBitmapFromFile( "216.png")

                .DrawBitmap(B, 1, Direct2D1. BitmapInterpolationMode.Linear, New Direct2D1. RectF(150, 150, 280, 280), New Direct2D1. RectF(0, 0, 260, 260))
                .DrawBitmapAtOrigin(B, 1, Direct2D1. BitmapInterpolationMode.Linear, New Direct2D1. RectF(10, 10, 130, 130))
                .EndDraw()
            End  With

            SaveToFile( "217.png")
        End  If
    End  Sub

    Public  Sub SaveToFile(FileName As  String)

        Dim size As Direct2D1. SizeU = _renderTarget.PixelSize
        Dim d2dBitmap As Direct2D1. D2DBitmap = _renderTarget.CreateBitmap(size, _
                                                                                                             New Direct2D1. BitmapProperties( _
                                                                                                                       New Direct2D1. PixelFormat( _
                                                                                                                                Graphics. Format.B8G8R8A8UNorm, _
                                                                                                                                Direct2D1. AlphaMode.Ignore), _
                                                                                                                        _renderTarget.Dpi.X, _renderTarget.Dpi.Y))


        d2dBitmap.CopyFromRenderTarget(_renderTarget)

        Dim wicBitmap As WIC. ImagingBitmap = _imagingFactory.CreateImagingBitmap( _
                                                                                             size.Width, size.Height, _
                                                                                             WIC. PixelFormats.Bgr32Bpp, _
                                                                                             WIC. BitmapCreateCacheOption.CacheOnLoad)
        Dim wicRenderTarget As Direct2D1. RenderTarget = _d2DFactory.CreateWicBitmapRenderTarget(wicBitmap, _renderProps)

        wicRenderTarget.BeginDraw()
        wicRenderTarget.DrawBitmap(d2dBitmap)
        wicRenderTarget.EndDraw()


        Dim fileType As  Guid

        Select  Case FileName.Substring(FileName.LastIndexOf( ".") + 1).ToUpper
            Case  "PNG"
                fileType = WIC. ContainerFormats.Png
            Case  "JPEG", "JPG"
                fileType = WIC. ContainerFormats.Jpeg
            Case  "GIF"
                fileType = WIC. ContainerFormats.Gif
            Case  "TIFF", "TIF"
                fileType = WIC. ContainerFormats.Tiff
            Case  "WMP"
                fileType = WIC. ContainerFormats.Wmp
            Case Else
                fileType = WIC. ContainerFormats.Bmp
        End  Select

        wicBitmap.SaveToFile(_imagingFactory, fileType, FileName)

    End  Sub
End  Class

上面的例子中,添加了一个LoadBitmapFromFile函数,负责把位图文件转换为D2DBitmap对象。在LoadBitmapFromFile函数中,一些枚举参数的设置都是采用默认值,最好不要改成其他值。还添加了一个SaveToFile方法,负责把RenderTarget对象上的内容保存到指定文件.

在Render方法中,先用LoadBitmapFromFile函数加载216.png,然后通过DrawBitmap和DrawBitmapAtOrigin方法绘制图片。并调用SaveToFile方法把RenderTarget上的内容保存到217.png文件中。

 

先看看216.png图片

来看看示例代码的效果图,来了解DrawBitmap和DrawBitmapAtOrigin方法的区别

 

 

 

 

位图笔刷(BitmapBrush)

在前文介绍了纯色笔刷(SolidColorBrush)、线性渐变笔刷(LinearGradientBrush)、径向渐变笔刷(RadialGradientBrush)

这里再介绍最后一种笔刷——位图笔刷(BitmapBrush)

 

先看看位图笔刷(BitmapBrush)对应的RenderTarget对象的CreateBitmapBrush函数的原型定义

 
Public  Function CreateBitmapBrush(bitmap As Direct2D1. D2DBitmap) As Direct2D1. BitmapBrush
Public  Function CreateBitmapBrush(bitmap As Direct2D1. D2DBitmap, brushProperties As Direct2D1. BrushProperties) As Direct2D1. BitmapBrush
Public  Function CreateBitmapBrush(bitmap As Direct2D1. D2DBitmap, bitmapBrushProperties As Direct2D1. BitmapBrushProperties) As Direct2D1. BitmapBrush
Public  Function CreateBitmapBrush(bitmap As Direct2D1. D2DBitmap, bitmapBrushProperties As Direct2D1. BitmapBrushProperties, brushProperties As Direct2D1. BrushProperties) As Direct2D1. BitmapBrush

Direct2D1. BitmapBrushProperties(extendModeX As Direct2D1. ExtendMode, extendModeY As Direct2D1. ExtendMode, interpolationMode As Direct2D1. BitmapInterpolationMode)

从上面的函数的原型定义来看,类型BitmapBrushProperties有三个参数,extendModeX指明位图笔刷(BitmapBrush)在水平扩展区域的扩展模式;extendModeY指明位图笔刷(BitmapBrush)在垂直扩展区域的扩展方式,扩展方式还是有3中,分别是Clamp(延伸:按照笔刷边界点的颜色延伸)、Wrap(换行:按笔刷的方向重新设置颜色)、Mirror(镜像:按笔刷的反方向重新设置颜色)。枚举BitmapInterpolationMode指明位图的插值算法。并且这三个参数在位图笔刷(BitmapBrush)对象中也有对应的属性,分别是ExtendModeX、ExtendModeY、InterpolationMode

 

我们先看看一个位图笔刷(BitmapBrush)的示例代码

 
Public  Class  clsDirect2DSample11
    Inherits  clsDirect2DSample

    Protected _imagingFactory As WIC. ImagingFactory

    Public  Sub  New()
        MyBase.New()
        _imagingFactory = WIC. ImagingFactory.Create
    End  Sub

    Public  Function LoadBitmapFromFile(fileName As  String, Optional frameIndex As  Integer = 0) As Direct2D1. D2DBitmap
        Dim decoder As WIC. BitmapDecoder = _imagingFactory.CreateDecoderFromFileName(fileName, WIC. DesiredAccess.Read, WIC. DecodeMetadataCacheOption.OnLoad)
        If frameIndex > decoder.FrameCount - 1 OrElse frameIndex < 0 Then frameIndex = 0
        Dim source As WIC. BitmapFrameDecode = decoder.GetFrame(frameIndex)
        Dim converter As WIC. FormatConverter = _imagingFactory.CreateFormatConverter()
        converter.Initialize(source.ToBitmapSource(), WIC. PixelFormats.Pbgra32Bpp, WIC. BitmapDitherType.None, WIC. BitmapPaletteType.MedianCut)
        Return _renderTarget.CreateBitmapFromWicBitmap(converter.ToBitmapSource())
    End  Function

    Public  Shadows  Sub Render()
        If  Not _renderTarget Is  Nothing  Then

            With _renderTarget
                .BeginDraw()

                .Clear( New Direct2D1. ColorF( Color.Chocolate.ToArgb))

                Dim B As Direct2D1. D2DBitmap = LoadBitmapFromFile( "216.png")
                Dim BB As Direct2D1. BitmapBrush = _renderTarget.CreateBitmapBrush(B)

                Dim R As  New Direct2D1. RectF(130, 130, 390, 390)
                Dim SB As Direct2D1. SolidColorBrush = _renderTarget.CreateSolidColorBrush( New Direct2D1. ColorF(0, 0, 0))

                .DrawRectangle(R, SB, 3)
                .FillRectangle(R, BB)

                .EndDraw()
            End  With
        End  If
    End  Sub
End  Class

 

上面的代码中,首先定义了一个位图笔刷(BitmapBrush),然后用该位图笔刷填充一个矩形。下面看看效果

image

 

效果有点出乎意料,设置因为,216.png这个位图大小为260*260px,在设置位图笔刷(BitmapBrush)时,默认笔刷的起始位置是画布的原点(左上角)。而由于矩形的范围是(130,130,390,390),因此位图笔刷只有一部分画在矩形的范围里(位图的右下角的部分),而由于默认的扩展模式是Clamp(延伸:按照笔刷边界点的颜色延伸),故在向右和向下延伸了边界的颜色。

 

如何更改位图笔刷的起始位置?通过位图笔刷(BitmapBrush)的Transform属性来更改起始位置。如下面的示例代码所示(有关Transform的详细内容留待后文再详解)

 
Public  Class  clsDirect2DSample12
    Inherits  clsDirect2DSample11

    Public  Shadows  Sub Render()
        If  Not _renderTarget Is  Nothing  Then

            With _renderTarget
                .BeginDraw()

                .Clear( New Direct2D1. ColorF( Color.Chocolate.ToArgb))

                Dim B As Direct2D1. D2DBitmap = LoadBitmapFromFile( "216.png")
                Dim BB As Direct2D1. BitmapBrush = _renderTarget.CreateBitmapBrush(B)

                BB.Transform = Direct2D1. Matrix3x2F.Translation(130, 130)

                Dim R As  New Direct2D1. RectF(130, 130, 390, 390)
                Dim SB As Direct2D1. SolidColorBrush = _renderTarget.CreateSolidColorBrush( New Direct2D1. ColorF(0, 0, 0))

                .DrawRectangle(R, SB, 3)
                .FillRectangle(R, BB)

                .EndDraw()
            End  With
        End  If
    End  Sub
End  Class

 

下面是示例代码的效果图,看看效果,了解如何更改位图笔刷(BitmapBrush)的起始位置

image

 

 

下面再看看另一个示例的代码,把前面的内容总结复习一下

 
Public  Class  clsDirect2DSample13
    Inherits  clsDirect2DSample11

    Public  Shadows  Sub Render()
        If  Not _renderTarget Is  Nothing  Then

            With _renderTarget
                .BeginDraw()

                .Clear( New Direct2D1. ColorF( Color.Chocolate.ToArgb))

                Dim PG As Direct2D1. PathGeometry
                Dim sink As Direct2D1. GeometrySink

                PG = _d2DFactory.CreatePathGeometry
                sink = PG.Open

                sink.BeginFigure( New Direct2D1. Point2F(30, 30), Direct2D1. FigureBegin.Filled)

                sink.AddLine( New Direct2D1. Point2F(240, 30))
                sink.AddArc( New Direct2D1. ArcSegment( _
                                                            New Direct2D1. Point2F(240, 240), _
                                                            New Direct2D1. SizeF(60, 100), _
                                                            45, _
                                                            Direct2D1. SweepDirection.Clockwise,
                                                            Direct2D1. ArcSize.Large))
                sink.AddBezier( New Direct2D1. BezierSegment( _
                                                                    New Direct2D1. Point2F(170, 120), _
                                                                    New Direct2D1. Point2F(100, 360), _
                                                                    New Direct2D1. Point2F(30, 240)))

                sink.EndFigure(Direct2D1. FigureEnd.Closed)
                sink.Close()

                Dim B As Direct2D1. D2DBitmap = LoadBitmapFromFile( "216.png")
                Dim BB As Direct2D1. BitmapBrush = _renderTarget.CreateBitmapBrush(B)
                Dim SB As Direct2D1. SolidColorBrush = _renderTarget.CreateSolidColorBrush( New Direct2D1. ColorF(0, 0, 0))

                BB.ExtendModeX = Direct2D1. ExtendMode.Mirror
                BB.ExtendModeY = Direct2D1. ExtendMode.Mirror
                BB.Transform = Direct2D1. Matrix3x2F.Scale(0.4, 0.4)

                .DrawGeometry(PG, SB, 3)
                .FillGeometry(PG, BB)

                .EndDraw()
            End  With
        End  If
    End  Sub
End  Class

上面的示例代码中,把位图笔刷(BitmapBrush)的水平扩展模式和垂直扩展模式都改成Mirror(镜像),并且把位图笔刷的大小改为原来的0.4倍。下面看看效果

image

 

 

 

最后,介绍一个非常好用的工具:ILSpy。文章中的很多原型定义都是直接从该工具中复制而来,省了不少的功夫。

相关文章
|
4月前
|
XML 算法 Java
Android App开发之位图加工Bitmap中转换位图的像素色彩、裁剪内部区域、利用矩阵变换位图的讲解及实战(附源码和演示)
Android App开发之位图加工Bitmap中转换位图的像素色彩、裁剪内部区域、利用矩阵变换位图的讲解及实战(附源码和演示)
29 0
|
存储 安全 索引
Bitmaps(位图)
什么是 Bitmaps Bitmaps 并不是实际的数据类型,而是定义在String类型上的一个面向字节操作的集合。因为字符串是二进制安全的块,他们的最大长度是512M,最适合设置成2^32个不同字节。 Bitmaps 的最大优势之一在存储信息时极其节约空间。例如,在一个以增量用户ID来标识不同用户的系统中,记录用户的四十亿的一个单独bit信息(例如,要知道用户是否想要接收最新的来信)仅仅使用512M内存。
 Bitmaps(位图)
|
存储 NoSQL Redis
Bitmaps-位图
Bitmaps-位图
130 0
Bitmaps-位图
Halcon把8位图转换为24位图的方法
Halcon把8位图转换为24位图的方法
391 0
|
Windows
[UWP]使用Writeable?Bitmap创建HSV色轮
原文:[UWP]使用Writeable?Bitmap创建HSV色轮 1. HSV 1.1 HSV的定义 HSV都是一种将RGB色彩模型中的点在圆柱坐标系中的表示法,这种表示法试图做到比RGB基于笛卡尔坐标系的几何结构更加直观。
1433 0