Unity引擎绘制多边形属性图

ops/2024/11/15 0:50:32/

  大家好,我是阿赵
  在制作游戏的时候,经常会遇到需要绘制多边形属性图的需求,比如这种效果:
可以根据需要的属性的数量变化多边形的边数,然后每一个顶点从中心点开始到多边形的顶点的长度代表了该属性的强度,然后多个属性的顶点连接起来形成一个多边形图形。
在这里插入图片描述

  这里介绍一下我自己的做法。

一、 需求拆分:

  需要做到上图的效果,可以拆分成几个部分

1、底板

在这里插入图片描述

  这一个图片,当然也可以用程序计算生成出来,但我个人觉得,这个底图除了会出现边数不一样以外,其他的样式差不多是固定的,而程序生成的图片可能没有美术做的底图好看,所以我是比较建议直接出图使用。如果需要换边数,可以直接换图

2、属性多边形

在这里插入图片描述

  这个形状是会根据属性的变化而变化的,所以是不能直接用美术出图的,必现要动态生成。既然是动态的绘制多边形,所以用到的知识点肯定是动态计算顶点和索引,构成Mesh网格来显示了。由于这个形状基本上都是用于UI的,所以不能直接用Mesh和MeshRenderer来渲染,我这里选择的是MaskableGraphic。
  MaskableGraphic可以直接在UI上通过顶点和索引绘制多边形。所以我们只需要固定一个中心点坐标,然后通过设置一些参数,比如:

  1. 边数
  2. 最大半径
  3. 颜色
  4. 属性的最大最小值
  5. 每一个边的当前值
  6. 等等
      然后根据参数计算几个顶点的坐标,再通过(中心点、当前顶点、下一个顶点)作为一个三角形的索引,就可以逐个三角形绘制出来了。最后指定颜色,半透明也是在颜色里面指定。

3、多边形描边

在这里插入图片描述

  这个的原理和上一步一样,都是使用MaskableGraphic来自定义顶点和索引绘制。
  可以设置的参数和上面绘制多边形基本一致,只需要加多:
1、 描边的厚度
2、 描边的颜色
  绘制描边的算法比绘制多边形本身复杂一些,我一开始想得简单,直接把多边形的半径扩大,然后往回减去描边厚度,来得到描边的一个角上的两个顶点。不过那样做是不行的,将会导致描边的线段不是均匀的厚度,到了尖角的地方会变得很厚。
  后来我还是老老实实的对多边形的每条边进行往内平移,并且求出每条平移后的线和下一条线的延伸线的交点,作为描边的第二层的顶点。然后如果有描边的情况下,多边形的顶点也是使用了描边的第二层的顶点,两者就完全接得上了。
  由于这个原因,所以需要求线段平移和交点的方法。

4、在编辑器调整效果

在这里插入图片描述

  最后,综合以上所述,在编辑器里面可以暴露这些参数,可以在做UI的时候就看到效果,并且用代码去设置。这里还有一个angle角度的变量,是用于旋转多边形的,因为有时候我们的需求多边形不一定是正上方有个顶点,可以是旋转一定角度。
  有一个isChange的变量,下面的代码里面会说到,只有在修改的情况下才会重新计算顶点。所以如果在编辑器的非运行状态下,可以手点一下激活,这样在调整参数的时候,画面就会立刻刷新变化。

二、 代码实现

1、 MaskableGraphic的基础代码

  使用MaskableGraphic之前介绍过,基础用法很简单,类继承MaskableGraphic,然后重写OnPopulateMesh方法,在里面输入顶点和索引列表。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;[RequireComponent(typeof(CanvasRenderer))][RequireComponent(typeof(RectTransform))]
public class UIPolyOutlineEx : MaskableGraphic
{private List<UIVertex> vertexList;private List<int> indexList;protected override void OnPopulateMesh(VertexHelper vh){vh.Clear();           if (vertexList != null && vertexList.Count > 0&&indexList!= null && indexList.Count > 0){vh.AddUIVertexStream(vertexList, indexList);}}
}

2、 设置变量

  根据刚才的分析,设置了变量

public int sideCount = 0;
public float radius = 100;
public float minVal = 0.1f;
public float maxVal = 1;
public float edgeLen = 10;
public float[] sideValueArr;
public float angle = 0;
public Color insideColor = Color.green;
public Color edgeColor = Color.black;public bool isChange = false;
private List<UIVertex> outlinePoints;
private List<UIVertex> insidePoints;
private List<UIVertex> vertexList;
private List<int> indexList;

3、 计算顶点

  需要根据参数,求出内部多边形和外部描边的顶点,中间还有一个平移线段的顶点信息,所以需要三个数组来计算。计算完之后,就把他们保存起来。这里我是直接保存成UIVertex的,由于描边和内部颜色不一样,所以UIVertex里面也记录了颜色,所以描边的顶点列表和内部的顶点列表是完全分开的,就算看着是同一个点,其实也是两边都保存。

    private void CreatePoints(){List<Vector2> outsidePoints = new List<Vector2>();List<Vector2> outsidePoints2 = new List<Vector2>();List<Vector2> moveLinePoints = new List<Vector2>();outlinePoints = new List<UIVertex>();insidePoints = new List<UIVertex>();if (sideCount < 3){return;}float offsetRate = edgeLen / radius;Rect rect = gameObject.GetComponent<RectTransform>().rect;UIVertex vex0 = UIVertex.simpleVert;vex0.position = new Vector3(rect.center.x, rect.center.y, 0);Vector3 uv0 = new Vector2(0.5f, 0.5f);vex0.uv0 = uv0;vex0.color = insideColor;insidePoints.Add(vex0);for (int i = 0; i < sideCount; i++){float val = 0;if (sideValueArr != null && i < sideValueArr.Length){val = sideValueArr[i];}else{val = 0;}float ang = 360f / sideCount * i + angle;ang = ang * Mathf.Deg2Rad;val = Mathf.Clamp(val, minVal, maxVal);float x = val * Mathf.Sin(ang);float y = val * Mathf.Cos(ang);outsidePoints.Add(new Vector2(x, y));}if (edgeLen <= 0){for (int i = 0; i < outsidePoints.Count; i++){UIVertex vex = UIVertex.simpleVert;vex.position = new Vector3(rect.center.x + outsidePoints[i].x * radius, rect.center.y + outsidePoints[i].y * radius, 0);Vector2 uv = new Vector2(outsidePoints[i].x, outsidePoints[i].y);uv *= 0.5f;uv += new Vector2(0.5f, 0.5f);vex.uv0 = uv;vex.color = insideColor;insidePoints.Add(vex);}}else{for (int i = 0; i < outsidePoints.Count; i++){Vector2 p0 = Vector2.zero;Vector2 p1 = outsidePoints[i];Vector2 p2;if (i < outsidePoints.Count - 1){p2 = outsidePoints[i + 1];}else{p2 = outsidePoints[0];}Vector2 foot = GetPointToLine(p0, p1, p2);Vector2 dir = p0 - foot;dir.Normalize();Vector2 p3 = p1 + dir * offsetRate;Vector2 p4 = p2 + dir * offsetRate;moveLinePoints.Add(p3);moveLinePoints.Add(p4);}for (int i = 0; i < outsidePoints.Count; i++){int ind1 = i * 2;int ind2 = i * 2 + 1;int ind3 = i * 2 - 2;int ind4 = i * 2 - 1;if (i == 0){ind3 = moveLinePoints.Count - 2;ind4 = moveLinePoints.Count - 1;}Vector2 crossPoint = GetLineCrossPoint(moveLinePoints[ind1], moveLinePoints[ind2], moveLinePoints[ind3], moveLinePoints[ind4]);print(moveLinePoints[ind1] * radius + "," + moveLinePoints[ind2] * radius + "|" + moveLinePoints[ind3] * radius + "," + moveLinePoints[ind4] * radius + "||" + crossPoint * radius);outsidePoints2.Add(crossPoint);}for (int i = 0; i < outsidePoints.Count; i++){UIVertex vex = UIVertex.simpleVert;vex.position = new Vector3(rect.center.x + outsidePoints2[i].x * radius, rect.center.y + outsidePoints2[i].y * radius, 0);Vector2 uv = new Vector2(outsidePoints2[i].x, outsidePoints2[i].y);uv *= 0.5f;uv += new Vector2(0.5f, 0.5f);vex.uv0 = uv;vex.color = insideColor;insidePoints.Add(vex);vex = UIVertex.simpleVert;vex.position = new Vector3(rect.center.x + outsidePoints[i].x * radius, rect.center.y + outsidePoints[i].y * radius, 0);uv = new Vector2(outsidePoints[i].x, outsidePoints[i].y);uv *= 0.5f;uv += new Vector2(0.5f, 0.5f);vex.uv0 = uv;vex.color = edgeColor;outlinePoints.Add(vex);vex = UIVertex.simpleVert;vex.position = new Vector3(rect.center.x + outsidePoints2[i].x * radius, rect.center.y + outsidePoints2[i].y * radius, 0);uv = new Vector2(outsidePoints2[i].x, outsidePoints2[i].y);uv *= 0.5f;uv += new Vector2(0.5f, 0.5f);vex.uv0 = uv;vex.color = edgeColor;outlinePoints.Add(vex);}}}private Vector2 GetPointToLine(Vector2 p0, Vector2 p1, Vector2 p2){Vector2 lineDir = p1 - p2;lineDir.Normalize();float x0 = p0.x;float y0 = p0.y;float x1 = p1.x;// -lineDir.x*10;float y1 = p1.y;// - lineDir.y * 10;float x2 = p2.x;// + lineDir.x * 10;float y2 = p2.y;// + lineDir.x * 10;if ((x1 == x0 && y1 == y0) || (x2 == x0 && y2 == y0)){return new Vector2(x0, y0);//点和线段一边重合}float k = 1;if (x1 != x2){k = (y2 - y1) / (x2 - x1);}float a = k;float b = -1;float c = y1 - k * x1;float d = Mathf.Abs(a * x0 + b * y0 + c) / Mathf.Sqrt(a * a + b * b);float px = (b * b * x0 - a * b * y0 - a * c) / (a * a + b * b);float py = (a * a * y0 - a * b * x0 - b * c) / (a * a + b * b);return new Vector2(px, py);}private Vector2 GetLineCrossPoint(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4){Vector2 dir1 = (p1 - p2).normalized;p1 += dir1;p2 -= dir1;Vector2 dir2 = (p3 - p4).normalized;p3 += dir2;p4 -= dir2;Vector2 bx = p4 - p3;float d1 = Mathf.Abs(CrossMulVec(bx, p1 - p3));float d2 = Mathf.Abs(CrossMulVec(bx, p2 - p3));float dx = d1 + d2;if (dx == 0){return p1;}float t = d1 / dx;Vector2 temp = (p2 - p1) * t;return p1 + temp;}private float CrossMulVec(Vector2 p1, Vector2 p2){return p1.x * p2.y - p2.x * p1.y;
}

4、 求构成多边形的点和索引

  由于绘制的时候,需要把描边和内部的所有顶点和索引合并在一起,所以这里需要再计算一次,把两个列表合并。

        private void CreateMeshParam(){vertexList = new List<UIVertex>();indexList = new List<int>();int insidePointCount = 0;if (insidePoints != null && insidePoints.Count > 0){insidePointCount = insidePoints.Count;for (int i = 0; i < insidePoints.Count; i++){vertexList.Add(insidePoints[i]);}List<int> tempList = new List<int>();if (sideCount > 2){int ind3 = 0;for (int i = 0; i < sideCount; i++){tempList.Add(0);tempList.Add(i + 1);ind3 = i + 2;if (ind3 > sideCount){ind3 = 1;}tempList.Add(ind3);}}indexList = tempList;}if (outlinePoints != null && outlinePoints.Count > 0){for (int i = 0; i < outlinePoints.Count; i++){vertexList.Add(outlinePoints[i]);}for (int i = 0; i < sideCount; i++){int ind1 = i * 2;int ind2 = i * 2 + 2;int ind3 = i * 2 + 1;int ind4 = i * 2 + 3;if (i == sideCount - 1){ind2 = 0;ind4 = 1;}indexList.Add(ind1 + insidePointCount);indexList.Add(ind2 + insidePointCount);indexList.Add(ind3 + insidePointCount);indexList.Add(ind3 + insidePointCount);indexList.Add(ind2 + insidePointCount);indexList.Add(ind4 + insidePointCount);}}}

5、 各种属性的GetSet

  在运行的时候,我们不会希望每一帧都需要重复去计算多边形的顶点和索引,希望只有参数变化的时候才重新计算。所以那些可以改变的参数,应该都做成GetSet方法,然后在Set方法的时候,把一个isChange的标记设置为true。那么当isChange为true的时候,才重新计算。

public void InitData(int sideCount,float r,float edge)
{SetSideCount(sideCount);radius = r;edgeLen = edge;isChange = true;
}
public void SetIsChange()
{isChange = true;
}
public void SetSideCount(int val)
{sideCount = val;float[] newArr = new float[val];if(sideValueArr!=null&&sideValueArr.Length>0){for(int i = 0;i<val;i++){if(sideValueArr.Length>val){newArr[i] = sideValueArr[i];}}}sideValueArr = newArr;isChange = true;
}public void SetValArr(float[] vals)
{if (sideValueArr == null){return;}for (int i = 0; i < sideValueArr.Length; i++){if (i < vals.Length){sideValueArr[i] = vals[i];}}isChange = true;
}public void SetOneVal(int index,float val)
{if(index<0){return;}if(sideValueArr != null&&sideValueArr.Length> index){sideValueArr[index] = val;}isChange = true;
}public void SetOneValByLua(double index, double val)
{int ind = Mathf.FloorToInt((float)index);float value = (float)val;SetOneVal(ind - 1, value);
}public void SetAngle(float ang)
{angle = ang;isChange = true;
}public void SetRange(float min,float max)
{minVal = min;maxVal = max;isChange = true;
}public void SetInsideColor(Color col)
{insideColor = col;isChange = true;
}public void SetOutlineColor(Color col)
{edgeColor = col;isChange = true;
}public void SetRadius(float val)
{radius = val;isChange = true;
}

  这是一个重新计算顶点和索引的方法,只有在isChange为true的时候,才会调用。

private void UpdatePoly()
{if (sideCount == 0){vertexList = null;return;}CreatePoints();CreateMeshParam();
}

6、 修改OnPopulateMesh方法

  在OnPopulateMesh里面判断isChange,然后调用UpdatePoly方法。

   protected override void OnPopulateMesh(VertexHelper vh){//base.OnPopulateMesh(vh);vh.Clear();if(isChange){UpdatePoly();if(Application.isPlaying)isChange = false;}if (vertexList != null && vertexList.Count > 0){vh.AddUIVertexStream(vertexList, indexList);}}

三、完整代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace azhao
{[RequireComponent(typeof(CanvasRenderer))][RequireComponent(typeof(RectTransform))]public class UIPolyOutlineEx : MaskableGraphic{public int sideCount = 0;public float radius = 100;public float minVal = 0.1f;public float maxVal = 1;public float edgeLen = 10;public float[] sideValueArr;public float angle = 0;public Color insideColor = Color.green;public Color edgeColor = Color.black;public bool isChange = false;private List<UIVertex> outlinePoints;private List<UIVertex> insidePoints;private List<UIVertex> vertexList;private List<int> indexList;// Start is called before the first frame updateprivate new IEnumerator Start(){UpdatePoly();yield return null;}public void InitData(int sideCount,float r,float edge){SetSideCount(sideCount);radius = r;edgeLen = edge;isChange = true;}public void SetIsChange(){isChange = true;}public void SetSideCount(int val){sideCount = val;float[] newArr = new float[val];if(sideValueArr!=null&&sideValueArr.Length>0){for(int i = 0;i<val;i++){if(sideValueArr.Length>val){newArr[i] = sideValueArr[i];}}}sideValueArr = newArr;isChange = true;}public void SetValArr(float[] vals){if (sideValueArr == null){return;}for (int i = 0; i < sideValueArr.Length; i++){if (i < vals.Length){sideValueArr[i] = vals[i];}}isChange = true;}public void SetOneVal(int index,float val){if(index<0){return;}if(sideValueArr != null&&sideValueArr.Length> index){sideValueArr[index] = val;}isChange = true;}public void SetOneValByLua(double index, double val){int ind = Mathf.FloorToInt((float)index);float value = (float)val;SetOneVal(ind - 1, value);}public void SetAngle(float ang){angle = ang;isChange = true;}public void SetRange(float min,float max){minVal = min;maxVal = max;isChange = true;}public void SetInsideColor(Color col){insideColor = col;isChange = true;}public void SetOutlineColor(Color col){edgeColor = col;isChange = true;}public void SetRadius(float val){radius = val;isChange = true;}private void UpdatePoly(){if (sideCount == 0){vertexList = null;return;}CreatePoints();CreateMeshParam();}private void CreatePoints(){List<Vector2> outsidePoints = new List<Vector2>();List<Vector2> outsidePoints2 = new List<Vector2>();List<Vector2> moveLinePoints = new List<Vector2>();outlinePoints = new List<UIVertex>();insidePoints = new List<UIVertex>();if (sideCount < 3){return;}float offsetRate = edgeLen / radius;Rect rect = gameObject.GetComponent<RectTransform>().rect;UIVertex vex0 = UIVertex.simpleVert;vex0.position = new Vector3(rect.center.x, rect.center.y, 0);Vector3 uv0 = new Vector2(0.5f, 0.5f);vex0.uv0 = uv0;vex0.color = insideColor;insidePoints.Add(vex0);for (int i = 0; i < sideCount; i++){float val = 0;if (sideValueArr != null && i < sideValueArr.Length){val = sideValueArr[i];}else{val = 0;}float ang = 360f / sideCount * i + angle;ang = ang * Mathf.Deg2Rad;val = Mathf.Clamp(val, minVal, maxVal);float x = val * Mathf.Sin(ang);float y = val * Mathf.Cos(ang);outsidePoints.Add(new Vector2(x, y));}if (edgeLen <= 0){for (int i = 0; i < outsidePoints.Count; i++){UIVertex vex = UIVertex.simpleVert;vex.position = new Vector3(rect.center.x + outsidePoints[i].x * radius, rect.center.y + outsidePoints[i].y * radius, 0);Vector2 uv = new Vector2(outsidePoints[i].x, outsidePoints[i].y);uv *= 0.5f;uv += new Vector2(0.5f, 0.5f);vex.uv0 = uv;vex.color = insideColor;insidePoints.Add(vex);}}else{for (int i = 0; i < outsidePoints.Count; i++){Vector2 p0 = Vector2.zero;Vector2 p1 = outsidePoints[i];Vector2 p2;if (i < outsidePoints.Count - 1){p2 = outsidePoints[i + 1];}else{p2 = outsidePoints[0];}Vector2 foot = GetPointToLine(p0, p1, p2);Vector2 dir = p0 - foot;dir.Normalize();Vector2 p3 = p1 + dir * offsetRate;Vector2 p4 = p2 + dir * offsetRate;moveLinePoints.Add(p3);moveLinePoints.Add(p4);}for (int i = 0; i < outsidePoints.Count; i++){int ind1 = i * 2;int ind2 = i * 2 + 1;int ind3 = i * 2 - 2;int ind4 = i * 2 - 1;if (i == 0){ind3 = moveLinePoints.Count - 2;ind4 = moveLinePoints.Count - 1;}Vector2 crossPoint = GetLineCrossPoint(moveLinePoints[ind1], moveLinePoints[ind2], moveLinePoints[ind3], moveLinePoints[ind4]);print(moveLinePoints[ind1] * radius + "," + moveLinePoints[ind2] * radius + "|" + moveLinePoints[ind3] * radius + "," + moveLinePoints[ind4] * radius + "||" + crossPoint * radius);outsidePoints2.Add(crossPoint);}for (int i = 0; i < outsidePoints.Count; i++){UIVertex vex = UIVertex.simpleVert;vex.position = new Vector3(rect.center.x + outsidePoints2[i].x * radius, rect.center.y + outsidePoints2[i].y * radius, 0);Vector2 uv = new Vector2(outsidePoints2[i].x, outsidePoints2[i].y);uv *= 0.5f;uv += new Vector2(0.5f, 0.5f);vex.uv0 = uv;vex.color = insideColor;insidePoints.Add(vex);vex = UIVertex.simpleVert;vex.position = new Vector3(rect.center.x + outsidePoints[i].x * radius, rect.center.y + outsidePoints[i].y * radius, 0);uv = new Vector2(outsidePoints[i].x, outsidePoints[i].y);uv *= 0.5f;uv += new Vector2(0.5f, 0.5f);vex.uv0 = uv;vex.color = edgeColor;outlinePoints.Add(vex);vex = UIVertex.simpleVert;vex.position = new Vector3(rect.center.x + outsidePoints2[i].x * radius, rect.center.y + outsidePoints2[i].y * radius, 0);uv = new Vector2(outsidePoints2[i].x, outsidePoints2[i].y);uv *= 0.5f;uv += new Vector2(0.5f, 0.5f);vex.uv0 = uv;vex.color = edgeColor;outlinePoints.Add(vex);}}}private Vector2 GetPointToLine(Vector2 p0, Vector2 p1, Vector2 p2){Vector2 lineDir = p1 - p2;lineDir.Normalize();float x0 = p0.x;float y0 = p0.y;float x1 = p1.x;// -lineDir.x*10;float y1 = p1.y;// - lineDir.y * 10;float x2 = p2.x;// + lineDir.x * 10;float y2 = p2.y;// + lineDir.x * 10;if ((x1 == x0 && y1 == y0) || (x2 == x0 && y2 == y0)){return new Vector2(x0, y0);//点和线段一边重合}float k = 1;if (x1 != x2){k = (y2 - y1) / (x2 - x1);}float a = k;float b = -1;float c = y1 - k * x1;float d = Mathf.Abs(a * x0 + b * y0 + c) / Mathf.Sqrt(a * a + b * b);float px = (b * b * x0 - a * b * y0 - a * c) / (a * a + b * b);float py = (a * a * y0 - a * b * x0 - b * c) / (a * a + b * b);return new Vector2(px, py);}private Vector2 GetLineCrossPoint(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4){Vector2 dir1 = (p1 - p2).normalized;p1 += dir1;p2 -= dir1;Vector2 dir2 = (p3 - p4).normalized;p3 += dir2;p4 -= dir2;Vector2 bx = p4 - p3;float d1 = Mathf.Abs(CrossMulVec(bx, p1 - p3));float d2 = Mathf.Abs(CrossMulVec(bx, p2 - p3));float dx = d1 + d2;if (dx == 0){return p1;}float t = d1 / dx;Vector2 temp = (p2 - p1) * t;return p1 + temp;}private float CrossMulVec(Vector2 p1, Vector2 p2){return p1.x * p2.y - p2.x * p1.y;}private void CreateMeshParam(){vertexList = new List<UIVertex>();indexList = new List<int>();int insidePointCount = 0;if (insidePoints != null && insidePoints.Count > 0){insidePointCount = insidePoints.Count;for (int i = 0; i < insidePoints.Count; i++){vertexList.Add(insidePoints[i]);}List<int> tempList = new List<int>();if (sideCount > 2){int ind3 = 0;for (int i = 0; i < sideCount; i++){tempList.Add(0);tempList.Add(i + 1);ind3 = i + 2;if (ind3 > sideCount){ind3 = 1;}tempList.Add(ind3);}}indexList = tempList;}if (outlinePoints != null && outlinePoints.Count > 0){for (int i = 0; i < outlinePoints.Count; i++){vertexList.Add(outlinePoints[i]);}for (int i = 0; i < sideCount; i++){int ind1 = i * 2;int ind2 = i * 2 + 2;int ind3 = i * 2 + 1;int ind4 = i * 2 + 3;if (i == sideCount - 1){ind2 = 0;ind4 = 1;}indexList.Add(ind1 + insidePointCount);indexList.Add(ind2 + insidePointCount);indexList.Add(ind3 + insidePointCount);indexList.Add(ind3 + insidePointCount);indexList.Add(ind2 + insidePointCount);indexList.Add(ind4 + insidePointCount);}}}// Update is called once per framevoid Update(){}protected override void OnPopulateMesh(VertexHelper vh){//base.OnPopulateMesh(vh);vh.Clear();if(isChange){UpdatePoly();if(Application.isPlaying)isChange = false;}if (vertexList != null && vertexList.Count > 0){vh.AddUIVertexStream(vertexList, indexList);}}}
}

http://www.ppmy.cn/ops/113373.html

相关文章

排序题目:三次操作后最大值与最小值的最小差

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;三次操作后最大值与最小值的最小差 出处&#xff1a;1509. 三次操作后最大值与最小值的最小差 难度 5 级 题目描…

Python数据分析案例60——扩展变量后的神经网络风速预测(tsfresh)

案例背景 时间序列的预测一直是经久不衰的实际应用和学术研究的对象&#xff0c;但是绝大多数的时间序列可能就没有太多的其他的变量&#xff0c;例如一个股票的股价&#xff0c;还有一个企业的用电量&#xff0c;人的血糖浓度等等&#xff0c;空气的质量&#xff0c;温度这些…

npm发布插件超级简单版

在开源的世界里&#xff0c;每个人都有机会成为贡献者&#xff0c;甚至是创新的引领者。您是否有过这样的想法&#xff1a;开发一个解决特定问题的小工具&#xff0c;让他成为其他开发者手中的利器&#xff1f;今天&#xff0c;我们就来一场实战训练&#xff0c;学习如何将你的…

零基础考过软考信息系统项目管理师经验分享

选择适合的课程&#xff1a;如果你是零基础&#xff0c;建议找一些专门针对新手的课程&#xff0c;讲解通俗易懂。 刷题至关重要&#xff1a;软考的题库很庞大&#xff0c;多做题是必须的。 做好笔记和复习&#xff1a;上课时要做好笔记&#xff0c;课后及时复习&#xff0c;…

高级编程--第七章 XML

1、目标 理解XML该你那及优势 回避那些格式良好的XML文档 了解XML中特殊字符的处理方式 了解解析器概念 了解DOM树节点构造 会使用Dom操作XML数据&#xff08;添加/保存&#xff09; 2、XML简介 XML&#xff08;EXtensible Markup Language&#xff09;,可扩展标记语言&…

【算法基础实验】图论-BellmanFord最短路径

理论知识 Bellman-Ford 和 Dijkstra 是两种用于计算加权图中最短路径的算法&#xff0c;它们在多个方面存在不同之处。下面是它们之间的主要区别&#xff1a; 1. 边权重的处理 Bellman-Ford&#xff1a; 能够处理带有负权重边的图&#xff0c;且可以检测负权重环&#xff08…

TopoDOT2024.1注册机 道路自动化提取 雷达点云数据

TopoDOT2024.1是一套成熟的点云数据处理及应用系统&#xff0c;全面具备点云数据的存储管理、精度检核、特征自动提取、智能分析、高效建模、成果输出等应用功能。TopoDOT在LiDAR数据应用领域有着多年的实战经验&#xff0c;用户在实际项目中长期使用&#xff0c;尤其在交通领域…

Android 系统开发人员的权限说明文档

文档地址&#xff1a;frameworks/base/core/java/android/permission/Permissions.md 大家在这个文档中看到实例&#xff1a;frameworks/base/core/res/AndroidManifest.xml 自己使用工具翻译&#xff0c;里面可能有错误&#xff0c;欢迎指正 正文 This document targets s…