ArcGIS Pro SDK (九)几何 12 多面体

ArcGIS Pro SDK (九)几何 12 多面体

文章目录

  • [ArcGIS Pro SDK (九)几何 12 多面体](#ArcGIS Pro SDK (九)几何 12 多面体)
    • [1 通过拉伸多边形或折线构建多面体](#1 通过拉伸多边形或折线构建多面体)
    • [2 多面体属性](#2 多面体属性)
    • [3 构建多面体](#3 构建多面体)
    • [4 通过MultipatchBuilderEx构建多面体](#4 通过MultipatchBuilderEx构建多面体)
    • [5 从另一个多面体构建多面体](#5 从另一个多面体构建多面体)
    • [6 从 3D 模型文件构建多面体](#6 从 3D 模型文件构建多面体)
    • [7 构建 3D 特殊多面体形状](#7 构建 3D 特殊多面体形状)
    • [8 创建基本材料](#8 创建基本材料)
    • [9 使用 JPEG 纹理创建基本材质](#9 使用 JPEG 纹理创建基本材质)
    • [10 使用未压缩纹理创建基本材质](#10 使用未压缩纹理创建基本材质)
    • [11 获取多面体的纹理图像](#11 获取多面体的纹理图像)
    • [12 获取多面体的法线坐标](#12 获取多面体的法线坐标)
    • [13 获取多面体的法线](#13 获取多面体的法线)
    • [14 获取多面体的材质属性](#14 获取多面体的材质属性)

环境:Visual Studio 2022 + .NET6 + ArcGIS Pro SDK 3.0

1 通过拉伸多边形或折线构建多面体

csharp 复制代码
// 构建一个多边形
string json = "{\"hasZ\":true,\"rings\":[[[0,0,0],[0,1,0],[1,1,0],[1,0,0],[0,0,0]]],\"spatialReference\":{\"wkid\":4326}}";
Polygon polygon = PolygonBuilderEx.FromJson(json);

// 通过偏移量拉伸多边形以创建多面体
Multipatch multipatch = GeometryEngine.Instance.ConstructMultipatchExtrude(polygon, 2);

// 一个不同的多边形
json = "{\"hasZ\":true,\"rings\":[[[0,0,1],[0,1,2],[1,1,3],[1,0,4],[0,0,1]]],\"spatialReference\":{\"wkid\":4326}}";
polygon = PolygonBuilderEx.FromJson(json);

// 在 z 值之间拉伸以创建多面体
multipatch = GeometryEngine.Instance.ConstructMultipatchExtrudeFromToZ(polygon, -10, 20);

// 沿着由坐标定义的轴拉伸以创建多面体
Coordinate3D coord = new Coordinate3D(10, 18, -10);
multipatch = GeometryEngine.Instance.ConstructMultipatchExtrudeAlongVector3D(polygon, coord);

// 构建一条折线
json = "{\"hasZ\":true,\"paths\":[[[400,800,1000],[800,1400,1500],[1200,800,2000],[1800,1800,2500],[2200,800,3000]]],\"spatialReference\":{\"wkid\":3857}}";
Polyline polyline = PolylineBuilderEx.FromJson(json);

// 拉伸到特定 z 值以创建多面体
multipatch = GeometryEngine.Instance.ConstructMultipatchExtrudeToZ(polyline, 500);

Coordinate3D fromCoord = new Coordinate3D(50, 50, -500);
Coordinate3D toCoord = new Coordinate3D(200, 50, 1000);

// 在两个坐标之间拉伸以创建多面体
multipatch = GeometryEngine.Instance.ConstructMultipatchExtrudeAlongLine(polyline, fromCoord, toCoord);

2 多面体属性

csharp 复制代码
// 标准几何属性
bool hasZ = multipatch.HasZ;
bool hasM = multipatch.HasM;
bool hasID = multipatch.HasID;
bool isEmpty = multipatch.IsEmpty;
var sr = multipatch.SpatialReference;

// 补丁(部分)的数量
int patchCount = multiPatch.PartCount;
// 点的数量
int pointCount = multiPatch.PointCount;

// 作为 MapPoints 获取点
ReadOnlyPointCollection points = multipatch.Points;
// 或作为 3D 坐标获取点
IReadOnlyList<Coordinate3D> coordinates = multipatch.Copy3DCoordinatesToList();


// 多面体材料
bool hasMaterials = multiPatch.HasMaterials;
int materialCount = multiPatch.MaterialCount;


// 多面体纹理
bool hasTextures = multiPatch.HasTextures;
int textureVertexCount = multiPatch.TextureVertexCount;

// 法线
bool hasNormals = multiPatch.HasNormals;


// 单个补丁的属性(如果 multipatch.PartCount > 0)
int patchPriority = multiPatch.GetPatchPriority(patchIndex);
PatchType patchType = multiPatch.GetPatchType(patchIndex);

// 补丁点
int patchPointCount = multiPatch.GetPatchPointCount(patchIndex);
int pointStartIndex = multiPatch.GetPatchStartPointIndex(patchIndex);
// 补丁点是从 pointStartIndex 到 pointStartIndex + patchPointCount 的 multipatch.Points 中的点 

// 如果多面体有材料
if (hasMaterials)
{
    // 补丁是否有材料?
    //   如果补丁没有材料,则 materialIndex = -1;
    //   如果补丁有材料,则 0 <= materialIndex < materialCount
    int materialIndex = multipatch.GetPatchMaterialIndex(patchIndex);


    // 单个材料的属性(如果 multipatch.MaterialCount > 0)
    var color = multipatch.GetMaterialColor(materialIndex);
    var edgeColor = multipatch.GetMaterialEdgeColor(materialIndex);
    var edgeWidth = multipatch.GetMaterialEdgeWidth(materialIndex);
    var shiness = multipatch.GetMaterialShininess(materialIndex);
    var percent = multipatch.GetMaterialTransparencyPercent(materialIndex);
    var cullBackFace = multipatch.IsMaterialCullBackFace(materialIndex);

    // 纹理属性
    bool isTextured = multipatch.IsMaterialTextured(materialIndex);
    if (isTextured)
    {
        int columnCount = multipatch.GetMaterialTextureColumnCount(materialIndex);
        int rowCount = multipatch.GetMaterialTextureRowCount(materialIndex);
        int bpp = multipatch.GetMaterialTextureBytesPerPixel(materialIndex);
        TextureCompressionType compressionType = multipatch.GetMaterialTextureCompressionType(materialIndex);
        var texture = multipatch.GetMaterialTexture(materialIndex);
    }
}

// 纹理坐标(如果 multipatch.HasTextures = true)
if (hasTextures)
{
    int numPatchTexturePoints = multiPatch.GetPatchTextureVertexCount(patchIndex);
    var coordinate2D = multiPatch.GetPatchTextureCoordinate(patchIndex, 0);

    ICollection<Coordinate2D> textureCoordinates = new List<Coordinate2D>(numPatchTexturePoints);
    multiPatch.GetPatchTextureCoordinates(patchIndex, ref textureCoordinates);
}


// 补丁法线(如果 multipatch.HasNormals = true)
if (hasNormals)
{
    // 法线坐标的数量 = multipatch.GetPatchPointCount(patchIndex)
    Coordinate3D patchNormal = multipatch.GetPatchNormal(patchIndex, 0);
    ICollection<Coordinate3D> normalCoordinates = new List<Coordinate3D>(patchPointCount);
    multipatch.GetPatchNormals(patchIndex, ref normalCoordinates);
}

3 构建多面体

csharp 复制代码
// 导出为二进制 XML
string binaryXml = multiPatch.ToBinaryXml();

// 从二进制 XML 导入 - 方法需要在 MCT 上运行
Multipatch binaryMultipatch = MultipatchBuilderEx.FromBinaryXml(binaryXml);

// XML 导出/导入
string xml = multiPatch.ToXml();
Multipatch xmlMultipatch = MultipatchBuilderEx.FromXml(xml);

// esriShape 导出/导入
byte[] buffer = multiPatch.ToEsriShape();
Multipatch esriPatch = MultipatchBuilderEx.FromEsriShape(buffer);

// 或者使用 GeometryEngine
Multipatch patchImport = GeometryEngine.Instance.ImportFromEsriShape(EsriShapeImportFlags.EsriShapeImportDefaults, buffer, multiPatch.SpatialReference) as Multipatch;

4 通过MultipatchBuilderEx构建多面体

csharp 复制代码
var coords_face1 = new List<Coordinate3D>()
{
    new Coordinate3D(12.495461061000071,41.902603910000039,62.552700000000186),
    new Coordinate3D(12.495461061000071,41.902603910000039,59.504700000004959),
    new Coordinate3D(12.495461061000071,41.902576344000067,59.504700000004959),
    new Coordinate3D(12.495461061000071,41.902603910000039,62.552700000000186),
    new Coordinate3D(12.495461061000071,41.902576344000067,59.504700000004959),
    new Coordinate3D(12.495461061000071,41.902576344000067,62.552700000000186),
};

var coords_face2 = new List<Coordinate3D>()
{
    new Coordinate3D(12.495461061000071, 41.902576344000067, 62.552700000000186),
    new Coordinate3D(12.495461061000071, 41.902576344000067, 59.504700000004959),
    new Coordinate3D(12.495488442000067, 41.902576344000067, 59.504700000004959),
    new Coordinate3D(12.495461061000071, 41.902576344000067, 62.552700000000186),
    new Coordinate3D(12.495488442000067, 41.902576344000067, 59.504700000004959),
    new Coordinate3D(12.495488442000067, 41.902576344000067, 62.552700000000186),
};

var coords_face3 = new List<Coordinate3D>()
{
    new Coordinate3D(12.495488442000067, 41.902576344000067, 62.552700000000186),
    new Coordinate3D(12.495488442000067, 41.902576344000067, 59.504700000004959),
    new Coordinate3D(12.495488442000067, 41.902603910000039, 59.504700000004959),
    new Coordinate3D(12.495488442000067, 41.902576344000067, 62.552700000000186),
    new Coordinate3D(12.495488442000067, 41.902603910000039, 59.504700000004959),
    new Coordinate3D(12.495488442000067, 41.902603910000039, 62.552700000000186),
};

var coords_face4 = new List<Coordinate3D>()
{
    new Coordinate3D(12.495488442000067, 41.902576344000067, 59.504700000004959),
    new Coordinate3D(12.495461061000071, 41.902576344000067, 59.504700000004959),
    new Coordinate3D(12.495461061000071, 41.902603910000039, 59.504700000004959),
    new Coordinate3D(12.495488442000067, 41.902576344000067, 59.504700000004959),
    new Coordinate3D(12.495461061000071, 41.902603910000039, 59.504700000004959),
    new Coordinate3D(12.495488442000067, 41.902603910000039, 59.504700000004959),
};

var coords_face5 = new List<Coordinate3D>()
{
    new Coordinate3D(12.495488442000067, 41.902603910000039, 59.504700000004959),
    new Coordinate3D(12.495461061000071, 41.902603910000039, 59.504700000004959),
    new Coordinate3D(12.495461061000071, 41.902603910000039, 62.552700000000186),
    new Coordinate3D(12.495488442000067, 41.902603910000039, 59.504700000004959),
    new Coordinate3D(12.495461061000071, 41.902603910000039, 62.552700000000186),
    new Coordinate3D(12.495488442000067, 41.902603910000039, 62.552700000000186),
};

var coords_face6 = new List<Coordinate3D>()
{
    new Coordinate3D(12.495488442000067, 41.902603910000039, 62.552700000000186),
    new Coordinate3D(12.495461061000071, 41.902603910000039, 62.552700000000186),
    new Coordinate3D(12.495461061000071, 41.902576344000067, 62.552700000000186),
    new Coordinate3D(12.495488442000067, 41.902603910000039, 62.552700000000186),
    new Coordinate3D(12.495461061000071, 41.902576344000067, 62.552700000000186),
    new Coordinate3D(12.495488442000067, 41.902576344000067, 62.552700000000186),
};

var materialRed = new BasicMaterial();
materialRed.Color = System.Windows.Media.Colors.Red;

var materialTransparent = new BasicMaterial();
materialTransparent.Color = System.Windows.Media.Colors.White;
materialTransparent.TransparencyPercent = 80;

var blueTransparent = new BasicMaterial(materialTransparent);
blueTransparent.Color = System.Windows.Media.Colors.SkyBlue;

// 创建补丁对象列表
var patches = new List<Patch>();

// 创建多面体构建器对象
var mpb = new ArcGIS.Core.Geometry.MultipatchBuilderEx();

// 使用适当的坐标制作每个补丁并添加到补丁列表中
var patch = mpb.MakePatch(PatchType.Triangles);
patch.Coords = coords_face1;
patches.Add(patch);

patch = mpb.MakePatch(PatchType.Triangles);
patch.Coords = coords_face2;
patches.Add(patch);

patch = mpb.MakePatch(PatchType.Triangles);
patch.Coords = coords_face3;
patches.Add(patch);

patch = mpb.MakePatch(PatchType.Triangles);
patch.Coords = coords_face4;
patches.Add(patch);

patch = mpb.MakePatch(PatchType.Triangles);
patch.Coords = coords_face5;
patches.Add(patch);

patch = mpb.MakePatch(PatchType.Triangles);
patch.Coords = coords_face6;
patches.Add(patch);

patches[0].Material = materialRed;
patches[1].Material = materialTransparent;
patches[2].Material = materialRed;
patches[3].Material = materialRed;
patches[4].Material = materialRed;
patches[5].Material = blueTransparent;

// 将补丁分配给多面体构建器
mpb.Patches = patches;

// 检查哪些补丁当前包含该材料
var red = mpb.QueryPatchIndicesWithMaterial(materialRed);
//   red should be [0, 2, 3, 4]


// 调用geometry函数获取多面体
multipatch = mpb.ToGeometry() as Multipatch;

5 从另一个多面体构建多面体

csharp 复制代码
// 创建多面体构建器对象
var builder = new ArcGIS.Core.Geometry.MultipatchBuilderEx(multipatch);

// 检查一些属性
bool hasM = builder.HasM;
bool hasZ = builder.HasZ;
bool hasID = builder.HasID;
bool isEmpty = builder.IsEmpty;
bool hasNormals = builder.HasNormals;

var patches = builder.Patches;
int patchCount = patches.Count;

// 如果有补丁
if (patchCount > 0)
{
    int pointCount = builder.GetPatchPointCount(0);

    // 替换第一个补丁中的第一个点
    if (pointCount > 0)
    {
        // 获取第一个点
        var pt = builder.GetPoint(0, 0);
        builder.SetPoint(0, 0, newPoint);
    }

    // 检查当前包含纹理的补丁
    var texture = builder.QueryPatchIndicesWithTexture(brickTextureResource);

    // 分配纹理材质
    patches[0].Material = brickMaterialTexture;
}

// 更新builder以支持M值
builder.HasM = true;
// 同步补丁属性以匹配builder属性
// 在这种情况下,因为我们刚刚将HasM设置为true,每个补丁现在都将为其坐标集获取一个默认的M值
builder.SynchronizeAttributeAwareness();

// 调用ToGeometry获取多面体
multipatch = builder.ToGeometry() as Multipatch;

// multipatch.HasM 将为 true

6 从 3D 模型文件构建多面体

csharp 复制代码
try
{
    var model = ArcGIS.Core.Geometry.MultipatchBuilderEx.From3DModelFile(@"c:\Temp\My3dModelFile.dae");
    bool modelIsEmpty = model.IsEmpty;
}
catch (FileNotFoundException)
{
    // 文件未找到
}
catch (ArgumentException)
{
    // 文件扩展名不受支持或无法读取文件
}

7 构建 3D 特殊多面体形状

csharp 复制代码
var sr = MapView.Active.Map.SpatialReference;

var extent = MapView.Active.Extent;
var center = extent.Center;
var centerZ = MapPointBuilderEx.CreateMapPoint(center.X, center.Y, 500, sr);

// 立方体
multipatch = ArcGIS.Core.Geometry.MultipatchBuilderEx.CreateMultipatch(MultipatchConstructType.Cube, centerZ, 200, sr);
// 四面体
multipatch = ArcGIS.Core.Geometry.MultipatchBuilderEx.CreateMultipatch(MultipatchConstructType.Tetrahedron, centerZ, 200, sr);
// 菱形
multipatch = ArcGIS.Core.Geometry.MultipatchBuilderEx.CreateMultipatch(MultipatchConstructType.Diamond, centerZ, 200, sr);
// 六角形
multipatch = ArcGIS.Core.Geometry.MultipatchBuilderEx.CreateMultipatch(MultipatchConstructType.Hexagon, centerZ, 200, sr);

// 球体框架
multipatch = ArcGIS.Core.Geometry.MultipatchBuilderEx.CreateMultipatch(MultipatchConstructType.SphereFrame, centerZ, 200, 0.8, sr);
// 球体
multipatch = ArcGIS.Core.Geometry.MultipatchBuilderEx.CreateMultipatch(MultipatchConstructType.Sphere, centerZ, 200, 0.8, sr);
// 圆柱体
multipatch = ArcGIS.Core.Geometry.MultipatchBuilderEx.CreateMultipatch(MultipatchConstructType.Cylinder, centerZ, 200, 0.8, sr);
// 圆锥体
multipatch = ArcGIS.Core.Geometry.MultipatchBuilderEx.CreateMultipatch(MultipatchConstructType.Cone, centerZ, 200, 0.8, sr);


// 使用builder添加材料或纹理
// 创建一个带有材料的圆锥体
builder = new MultipatchBuilderEx(MultipatchConstructType.Cone, centerZ, 200, 0.8, sr);

BasicMaterial faceMaterial = new BasicMaterial();
faceMaterial.Color = System.Windows.Media.Color.FromRgb(255, 0, 0);
faceMaterial.Shininess = 150;
faceMaterial.TransparencyPercent = 50;
faceMaterial.EdgeWidth = 20;

foreach (var patch in builder.Patches)
    patch.Material = faceMaterial;

multipatch = builder.ToGeometry() as Multipatch;

8 创建基本材料

csharp 复制代码
// 使用默认值创建BasicMaterial
BasicMaterial material = new BasicMaterial();
System.Windows.Media.Color color = material.Color;         // color = Colors.Black
System.Windows.Media.Color edgeColor = material.EdgeColor; // edgeColor = Colors.Black
int edgeWidth = material.EdgeWidth;                        // edgeWidth = 0
int transparency = material.TransparencyPercent;           // transparency = 0
int shininess = material.Shininess;                        // shininess = 0
bool cullBackFace = material.IsCullBackFace;               // cullBackFace = false

// 修改属性
material.Color = System.Windows.Media.Colors.Red;
material.EdgeColor = System.Windows.Media.Colors.Blue;
material.EdgeWidth = 10;
material.TransparencyPercent = 50;
material.Shininess = 25;
material.IsCullBackFace = true;

9 使用 JPEG 纹理创建基本材质

csharp 复制代码
// 将jpeg读取到缓冲区中
// 在3.0版本中需要https://www.nuget.org/packages/Microsoft.Windows.Compatibility
// System.Drawing
System.Drawing.Image image = System.Drawing.Image.FromFile(@"C:\temp\myImageFile.jpg");
MemoryStream memoryStream = new MemoryStream();

System.Drawing.Imaging.ImageFormat format = System.Drawing.Imaging.ImageFormat.Jpeg;
image.Save(memoryStream, format);
byte[] imageBuffer = memoryStream.ToArray();

var jpgTexture = new JPEGTexture(imageBuffer);

// 纹理属性
int bpp = jpgTexture.BytesPerPixel;
int columnCount = jpgTexture.ColumnCount;
int rowCount = jpgTexture.RowCount;

// 构建textureResource和material
BasicMaterial material = new BasicMaterial();
material.TextureResource = new TextureResource(jpgTexture);

10 使用未压缩纹理创建基本材质

csharp 复制代码
UncompressedTexture uncompressedTexture1 = new UncompressedTexture(new byte[10 * 12 * 3], 10, 12, 3);

// 纹理属性
int bpp = uncompressedTexture1.BytesPerPixel;
int columnCount = uncompressedTexture1.ColumnCount;
int rowCount = uncompressedTexture1.RowCount;

// 构建textureResource和material
TextureResource tr = new TextureResource(uncompressedTexture1);
BasicMaterial material = new BasicMaterial();
material.TextureResource = tr;

11 获取多面体的纹理图像

csharp 复制代码
// <summary>
// 此方法获取多面体的材料纹理图像。
// 此方法必须在MCT上调用。使用QueuedTask.Run。
// </summary>
// <param name="multipatch">输入的多面体。</param>
// <param name="patchIndex">获取材料纹理的补丁(部分)索引。</param>
public void GetMultipatchTextureImage(Multipatch multipatch, int patchIndex)
{
    int materialIndex = multipatch.GetPatchMaterialIndex(patchIndex);
    if (!multipatch.IsMaterialTextured(materialIndex))
        return;

    TextureCompressionType compressionType = 
        multipatch.GetMaterialTextureCompressionType(materialIndex);

    string ext = compressionType == TextureCompressionType.CompressionJPEG ? ".jpg" : ".dat";
    byte[] textureBuffer = multipatch.GetMaterialTexture(materialIndex);

    Stream imageStream = new MemoryStream(textureBuffer);
    System.Drawing.Image image = System.Drawing.Image.FromStream(imageStream);
    image.Save(@"C:\temp\myImage" + ext);
}

12 获取多面体的法线坐标

csharp 复制代码
// <summary>
// 此方法获取多面体的法线坐标并执行一些操作。
// 此方法必须在MCT上调用。使用QueuedTask.Run。
// </summary>
// <param name="multipatch">输入的多面体。</param>
// <param name="patchIndex">获取法线的补丁(部分)索引。</param>
public void DoSomethingWithNormalCoordinate(Multipatch multipatch, int patchIndex)
{
    if (multipatch.HasNormals)
    {
        // 如果多面体有法线,则法线数量等于点的数量。
        int numNormals = multipatch.GetPatchPointCount(patchIndex);

        for (int pointIndex = 0; pointIndex < numNormals; pointIndex++)
        {
            Coordinate3D normal = multipatch.GetPatchNormal(patchIndex, pointIndex);

            // 对法线坐标进行一些操作。
        }
    }
}

13 获取多面体的法线

csharp 复制代码
// <summary>
// 此方法获取多面体的法线坐标并执行一些操作。
// 此方法必须在MCT上调用。使用QueuedTask.Run。
// </summary>
// <param name="multipatch">输入的多面体。</param>
public void DoSomethingWithNormalCoordinates(Multipatch multipatch)
{
    if (multipatch.HasNormals)
    {
        // 只分配一次列表
        int numPoints = multipatch.PointCount;
        ICollection<Coordinate3D> normals = new List<Coordinate3D>(numPoints);

        // 多面体的部分也称为补丁
        int numPatches = multipatch.PartCount;

        for (int patchIndex = 0; patchIndex < numPatches; patchIndex++)
        {
            multipatch.GetPatchNormals(patchIndex, ref normals);

            // 对这个补丁的法线进行一些操作。
        }
    }
}

14 获取多面体的材质属性

csharp 复制代码
public void GetMaterialProperties(Multipatch multipatch, int patchIndex)
{
    if (multipatch.HasMaterials)
    {
        // 获取指定补丁的材质索引。
        int materialIndex = multipatch.GetPatchMaterialIndex(patchIndex);

        System.Windows.Media.Color color = multipatch.GetMaterialColor(materialIndex);
        int tranparencyPercent = multipatch.GetMaterialTransparencyPercent(materialIndex);
        bool isBackCulled = multipatch.IsMaterialCullBackFace(materialIndex);

        if (multipatch.IsMaterialTextured(materialIndex))
        {
            int bpp = multipatch.GetMaterialTextureBytesPerPixel(materialIndex);
            int columnCount = multipatch.GetMaterialTextureColumnCount(materialIndex);
            int rowCount = multipatch.GetMaterialTextureRowCount(materialIndex);
        }
    }
}
相关推荐
△曉風殘月〆2 小时前
WPF MVVM入门系列教程(二、依赖属性)
c#·wpf·mvvm
逐·風4 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#
m0_656974747 小时前
C#中的集合类及其使用
开发语言·c#
九鼎科技-Leo7 小时前
了解 .NET 运行时与 .NET 框架:基础概念与相互关系
windows·c#·.net
九鼎科技-Leo9 小时前
什么是 ASP.NET Core?与 ASP.NET MVC 有什么区别?
windows·后端·c#·asp.net·mvc·.net
.net开发9 小时前
WPF怎么通过RestSharp向后端发请求
前端·c#·.net·wpf
小乖兽技术9 小时前
C#与C++交互开发系列(二十):跨进程通信之共享内存(Shared Memory)
c++·c#·交互·ipc
幼儿园园霸柒柒10 小时前
第七章: 7.3求一个3*3的整型矩阵对角线元素之和
c语言·c++·算法·矩阵·c#·1024程序员节
平凡シンプル12 小时前
C# EF 使用
c#
丁德双12 小时前
winform 加载 office excel 插入QRCode图片如何设定位置
c#·excel