【Unity3D】绘制物体外框线条盒子

news2024/11/20 12:45:29

1 需求描述

        点选物体、框选物体、绘制外边框 中介绍了物体投影到屏幕上的二维外框绘制方法,本文将介绍物体外框线条盒子绘制方法。

  • 内框:选中物体后,绘制物体的内框(紧贴物体、并与物体姿态一致的内框盒子)
  • 外框:选中物体后,绘制物体的外框(紧贴物体、并与世界坐标系的朝向一致的外框盒子)

        内框和外框效果如下,其中,黄色线框是内框,绿色线框是外框。

        本文完整代码见→Unity3D绘制物体外框线条盒子

2 需求实现

        1)原理

        获取物体外框盒子(Bounds)的方法主要有:

Bounds bounds = obj.GetComponent<MeshFilter>().mesh.bounds;
Bounds bounds = obj.GetComponent<Renderer>().bounds;
Bounds bounds = obj.GetComponent<Collider>().bounds;

        MeshFilter、Render、Collider 获取的 Bounds 区别如下:

  • MeshFilter Bounds:模型原始 mesh 的 Bounds(局部坐标系下的坐标),在 Transform 组件中修改缩放,不会影响其值大小,还原其真实渲染值大小(世界坐标系下的坐标)需要通过transform.TransformPoint(vertex) 变换还原;
  • Renderer Bounds:模型渲染的真实 Bounds(世界坐标系下的坐标),其姿态与世界坐标系的坐标轴朝向保持一致,在 Transform 组件中修改缩放,会影响其值大小;
  • Collider Bounds:模型碰撞体的 Bounds(世界坐标系下的坐标),其姿态与世界坐标系的坐标轴朝向保持一致,在 Transform 组件中修改缩放,会影响其值大小,如果碰撞体与模型表面完全吻合,其 Bounds 与 Renderer 的 Bounds 保持一致。

        本文通过 MeshFilter Bounds 绘制内框盒子,通过 Renderer Bounds 绘制外框盒子。

        2)场景对象

        说明:需要删除 Plane 对象的碰撞体。 

        3)代码

        EventDetector.cs

using UnityEngine;
 
public class EventDetector : MonoBehaviour { // 事件检测器
    private MyEventType eventType = MyEventType.None; // 事件类型
    private MyEventType lastEventType = MyEventType.None; // 上次事件类型
    private float scroll; // 滑轮滑动刻度
    private bool detecting; // 事件检测中
    private Vector3 clickDownMousePos; // 鼠标按下时的坐标
    private const float dragThreshold = 1; // 识别为拖拽的鼠标偏移
 
    private void Update() {
        detecting = true;
        DetectMouseEvent();
        DetectScrollEvent();
        UpgradeMouseEvent();
        detecting = false;
        lastEventType = eventType;
    }
 
    private void DetectMouseEvent() { // 检测鼠标事件
        if (Input.GetMouseButtonDown(0)) { // Click Down
            eventType = MyEventType.ClickDown;
            clickDownMousePos = Input.mousePosition;
        } else if (Input.GetMouseButtonUp(0)) {
            if (IsDragEvent(eventType)) { // End Drag
                eventType = MyEventType.EndDrag;
            } else { // Click Up
                eventType = MyEventType.ClickUp;
            }
        } else if (Input.GetMouseButton(0)) {
            if (IsDragEvent(eventType)) { // Drag
                eventType = MyEventType.Drag;
            } else if (Vector3.Distance(clickDownMousePos, Input.mousePosition) > dragThreshold) { // Begin Drag
                eventType = MyEventType.BeginDrag;
            } else { // Click
                eventType = MyEventType.Click;
            }
        } else {
            eventType = MyEventType.None;
        }
    }
 
    private void DetectScrollEvent() { // 检测滑轮事件
        if (eventType != MyEventType.None
            && (!IsBeginEvent(eventType) || lastEventType != MyEventType.None && !IsScrollEvent(lastEventType))) {
            scroll = 0;
            return;
        }
        float temScroll = Input.GetAxis("Mouse ScrollWheel");
        if (Mathf.Abs(scroll) < float.Epsilon && Mathf.Abs(temScroll) > float.Epsilon) { // Begin Scroll
            eventType = MyEventType.BeginScroll;
            scroll = temScroll;
        } else if (Mathf.Abs(scroll) > float.Epsilon && Mathf.Abs(temScroll) < float.Epsilon) { // End Scroll
            eventType = MyEventType.EndScroll;
            scroll = temScroll;
        } else if (Mathf.Abs(temScroll) > float.Epsilon) { // Scroll
            eventType = MyEventType.Scroll;
            scroll = temScroll;
        } else {
            scroll = 0;
        }
    }
 
    private void UpgradeMouseEvent() { // 升级鼠标事件(关联键盘事件)
        if (eventType == MyEventType.None) {
            return;
        }
        if (IsBeginEvent(eventType)) {
            if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)) {
                AddKeyType("Ctrl");
            } else if (Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt)) {
                AddKeyType("Alt");
            }
        } else {
            ContinueKeyType(); // 保持按键事件
        }
    }
 
    public MyEventType EventType() { // 事件类型
        if (detecting) {
            return lastEventType;
        }
        return eventType;
    }
 
    public bool HasClickEvent() { // 是否有点击事件
        MyEventType type = EventType();
        return IsClickEvent(type);
    }
 
    public bool HasDragEvent() { // 是否有拖拽事件
        MyEventType type = EventType();
        return IsDragEvent(type);
    }
 
    public bool HasScrollEvent() { // 是否有滑轮事件
        MyEventType type = EventType();
        return IsScrollEvent(type);
    }

    public bool HasCtrlScrollEvent() { // 是否有Ctrl滑轮事件
        MyEventType type = EventType();
        return type >= MyEventType.BeginCtrlScroll && type <= MyEventType.EndCtrlScroll;
    }
 
    public bool IsBeginDrag() { // 是否是开始拖拽类型事件
        MyEventType type = EventType();
        return type == MyEventType.BeginDrag || type == MyEventType.BeginCtrlDrag || type == MyEventType.BeginAltDrag;
    }
 
    public float Scroll() { // 鼠标滑轮滑动刻度
        if (HasScrollEvent()) {
            return scroll;
        }
        return 0;
    }
 
    private bool IsClickEvent(MyEventType type) { // 是否是点击事件
        return type >= MyEventType.ClickDown && type <= MyEventType.CtrlClickUp;
    }
 
    private bool IsDragEvent(MyEventType type) { // 是否是拖拽事件
        return type >= MyEventType.BeginDrag && type <= MyEventType.EndAltDrag;
    }
 
    private bool IsScrollEvent(MyEventType type) { // 是否是滑轮事件
        return type >= MyEventType.BeginScroll && type <= MyEventType.EndCtrlScroll;
    }
 
    private bool IsBeginEvent(MyEventType type) { // 是否是开始类型事件
        return type == MyEventType.ClickDown
            || type == MyEventType.BeginDrag
            || type == MyEventType.BeginCtrlDrag
            || type == MyEventType.BeginAltDrag
            || type == MyEventType.BeginScroll
            || type == MyEventType.BeginCtrlScroll;
    }
 
    private bool HasCtrlKey(MyEventType type) { // 是否有Ctrl按键事件
        return type >= MyEventType.CtrlClickDown && type <= MyEventType.CtrlClickUp
            || type >= MyEventType.BeginCtrlDrag && type <= MyEventType.EndCtrlDrag
            || type >= MyEventType.BeginCtrlScroll && type <= MyEventType.EndCtrlScroll;
    }
 
    private bool HasAltKey(MyEventType type) { // 是否有Alt按键事件
        return type >= MyEventType.BeginAltDrag && type <= MyEventType.EndAltDrag;
    }
 
    private void ContinueKeyType() { // 保持按键事件
        if (HasCtrlKey(lastEventType)) {
            AddKeyType("Ctrl");
        } else if (HasAltKey(lastEventType)) {
            AddKeyType("Alt");
        }
    }
 
    private void AddKeyType(string key) { // 添加按键事件
        if ("Ctrl".Equals(key)) {
            if (eventType == MyEventType.ClickDown) { // 点击事件
                eventType = MyEventType.CtrlClickDown;
            } else if (eventType == MyEventType.Click) {
                eventType = MyEventType.CtrlClick;
            } else if (eventType == MyEventType.ClickUp) {
                eventType = MyEventType.CtrlClickUp;
            } else if (eventType == MyEventType.BeginDrag) { // 拖拽事件
                eventType = MyEventType.BeginCtrlDrag;
            } else if (eventType == MyEventType.Drag) {
                eventType = MyEventType.CtrlDrag;
            } else if (eventType == MyEventType.EndDrag) {
                eventType = MyEventType.EndCtrlDrag;
            } else if (eventType == MyEventType.BeginScroll) { // 滑轮事件
                eventType = MyEventType.BeginCtrlScroll;
            } else if (eventType == MyEventType.Scroll) {
                eventType = MyEventType.CtrlScroll;
            } else if (eventType == MyEventType.EndScroll) {
                eventType = MyEventType.EndCtrlScroll;
            }
        } else if ("Alt".Equals(key)) {
            if (eventType == MyEventType.BeginDrag) { // 拖拽事件
                eventType = MyEventType.BeginAltDrag;
            } else if (eventType == MyEventType.Drag) {
                eventType = MyEventType.AltDrag;
            } else if (eventType == MyEventType.EndDrag) {
                eventType = MyEventType.EndAltDrag;
            }
        }
    }
}
 
public enum MyEventType { // 事件类型
    None = 0,
    ClickDown = 1,
    Click = 2,
    ClickUp = 3,
    CtrlClickDown = 4,
    CtrlClick = 5,
    CtrlClickUp = 6,
    BeginDrag = 10,
    Drag = 11,
    EndDrag = 12,
    BeginCtrlDrag = 13,
    CtrlDrag = 14,
    EndCtrlDrag = 15,
    BeginAltDrag = 16,
    AltDrag = 17,
    EndAltDrag = 18,
    BeginScroll = 20,
    Scroll = 21,
    EndScroll = 22,
    BeginCtrlScroll = 23,
    CtrlScroll = 24,
    EndCtrlScroll = 25
}

        说明: EventDetector 脚本组件挂在相机下,用于统一管理事件。点选物体(ClickUp)、滑动选框(Drag)、场景变换(Ctrl + Drag / Alt + Drag)都有鼠标事件,这些事件相互冲突,不便于在每个类里都去捕获鼠标和键盘事件,因此需要 EventDetector 统一管理事件。

        ClickSelect.cs

using UnityEngine;

public class ClickSelect : MonoBehaviour {
    private EventDetector eventDetector; // 鼠标事件检测器
    private LineBoxPainder lineBoxPainder;
    private Transform target; // 选中的目标
    private RaycastHit hit; // 碰撞信息

    private void Awake() {
        eventDetector = Camera.main.GetComponent<EventDetector>();
        lineBoxPainder = LineBoxPainder.GetInstance();
    }

    private void Update() {
        if (eventDetector.EventType() == MyEventType.ClickUp) {
            Transform temp = GetHitTrans();
            UpdateColor(target, temp);
            target = temp;
            if (target != null) {
                lineBoxPainder.DrawLineBox(target.gameObject);
            }
            else {
                lineBoxPainder.DrawLineBox(null);
            }
        }
    }

    private void UpdateColor(Transform old, Transform now) { // 更新颜色
        if (old != now) {
            if (old != null) {
                old.GetComponent<Renderer>().material.color = Color.gray;
            }
            if (now != null) {
                now.GetComponent<Renderer>().material.color = Color.red;
            }
        }
    }

    private Transform GetHitTrans() { // 获取屏幕射线碰撞的物体
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        if (Physics.Raycast(ray, out hit)) {
            return hit.transform;
        }
        return null;
    }
}

        说明:ClickSelect 脚本组件挂在 Work 下。

        LineBoxPainder.cs

using UnityEngine;

public class LineBoxPainder { // 线段盒子渲染器(每个线段盒子由4个矩形组成)
    private static LineBoxPainder instance; // 单例
    private GameObject lineParent; // 线条盒子父对象
    private LineRenderer[][] lineRenderers; // 线段渲染器
    private Material lineMaterial; // 线段材质

    private LineBoxPainder() {
        lineMaterial = new Material(Shader.Find("Hidden/Internal-Colored"));
        lineParent = new GameObject("LineBoxPainder");
        lineRenderers = new LineRenderer[2][];
        lineRenderers[0] = GetLineRenderers("InnerBox", Color.yellow);
        lineRenderers[1] = GetLineRenderers("OuterBox", Color.green);
    }

    public static LineBoxPainder GetInstance() { // 获取单例
        if (instance == null) {
            instance = new LineBoxPainder();
        }
        return instance;
    }

    public void DrawLineBox(GameObject obj) { // 绘制内框盒子和外框盒子
        Vector3[] InnerVertices = BoxProvider.GetInnerBox(obj);
        DrawBox(lineRenderers[0], InnerVertices); // 绘制内框盒子
        Vector3[] OuterVertices = BoxProvider.GetOuterBox(obj);
        DrawBox(lineRenderers[1], OuterVertices); // 绘制外框盒子
    }

    private LineRenderer[] GetLineRenderers(string name, Color color) { // 获取线段渲染器
        Material material = new Material(lineMaterial);
        material.color = color;
        GameObject box = new GameObject(name);
        box.transform.parent = lineParent.transform;
        LineRenderer[] lines = new LineRenderer[4];
        for (int i = 0; i < lines.Length; i++) {
            GameObject line = new GameObject("Line" + i);
            line.transform.parent = box.transform;
            lines[i] = line.AddComponent<LineRenderer>();
            lines[i].material = material;
            lines[i].textureMode = LineTextureMode.Tile;
            lines[i].widthMultiplier = 0.2f;
            lines[i].startWidth = 0.05f;
            lines[i].endWidth = 0.05f;
            lines[i].positionCount = 0;
            lines[i].loop = true;
        }
        return lines;
    }

    private void DrawBox(LineRenderer[] lines, Vector3[] vertices) { // 绘制一个长方体线段盒子, 每个盒子由4个矩形组成
        if (vertices == null || vertices.Length == 0) {
            for (int i = 0; i < 4; i++) { // 清空线段顶点
                lines[i].positionCount = 0;
            }
            return;
        }
        else
        {
            for (int i = 0; i < 4; i++) { // 初始化线段顶点
                lines[i].positionCount = 4;
            }
        }
        for (int i = 0; i < 4; i++) { // 计算每个矩形的顶点序列
            lines[0].SetPosition(i, vertices[i]);
            lines[1].SetPosition(i, vertices[i + 4]);
            if (i < 2) {
                lines[2].SetPosition(i, vertices[i]);
                lines[3].SetPosition(i, vertices[i + 2]);
            } else {
                lines[2].SetPosition(i, vertices[7 -i]);
                lines[3].SetPosition(i, vertices[9 -i]);
            }
        }
    }
}

        说明:LineBoxPainder 用于绘制内框和外框线段盒子,每个盒子使用 4 个 LineRenderer 渲染(对应 4 个矩形),每个 LineRenderer 有 4 个顶点,并设置为 loop,用于渲染一个矩形,一个长方体需要 4 个矩形拼成

        BoxProvider.cs

using UnityEngine;

public class BoxProvider { // 盒子提供者
    public static Vector3[] GetInnerBox(GameObject obj) { // 获取内框盒子8个顶点数据
        if (obj == null || obj.GetComponent<MeshFilter>() == null) {
            return null;
        }
        Bounds bounds = obj.GetComponent<MeshFilter>().mesh.bounds;
        Vector3[] vertices = GetBoxVertices(bounds);
        for (int i = 0; i < vertices.Length; i++) { // 将局部坐标转换为世界坐标
            vertices[i] = obj.transform.TransformPoint(vertices[i]);
        }
        return vertices;
    }

    public static Vector3[] GetOuterBox(GameObject obj) { // 获取外框盒子8个顶点数据
        if (obj == null || obj.GetComponent<Renderer>() == null) {
            return null;
        }
        Bounds bounds = obj.GetComponent<Renderer>().bounds;
        //Bounds bounds = obj.GetComponent<Collider>().bounds;
        Vector3[] vertices = GetBoxVertices(bounds);
        return vertices;
    }

    private static Vector3[] GetBoxVertices(Bounds bounds) { // 根据中心坐标和半边长计算8个顶点的数据
        Vector3 center = bounds.center;
        Vector3 extents = bounds.extents;
        Vector3[] vertices = new Vector3[8];
        vertices[0] = center + new Vector3(extents.x, extents.y, extents.z);
        vertices[1] = center + new Vector3(extents.x, extents.y, -extents.z);
        vertices[2] = center + new Vector3(extents.x, -extents.y, -extents.z);
        vertices[3] = center + new Vector3(extents.x, -extents.y, extents.z);
        vertices[4] = center + new Vector3(-extents.x, extents.y, extents.z);
        vertices[5] = center + new Vector3(-extents.x, extents.y, -extents.z);
        vertices[6] = center + new Vector3(-extents.x, -extents.y, -extents.z);
        vertices[7] = center + new Vector3(-extents.x, -extents.y, extents.z);
        return vertices;
    }
}

        说明:BoxProvider 用于计算物体外框的 8 个顶点序列。本文通过 MeshFilter Bounds 绘制内框盒子,通过 Renderer Bounds 绘制外框盒子。

        SceneController.cs

using System;
using UnityEngine;
 
public class SceneController : MonoBehaviour { // 场景变换控制器
    private EventDetector eventDetector; // 鼠标事件检测器
    public Action camChangedHandler; // 相机改变处理器
    private Transform cam; // 相机
    private float nearPlan; // 近平面
    private Vector3 preMousePos; // 上一帧的鼠标坐标
 
    private void Awake() {
        cam = Camera.main.transform;
        nearPlan = Camera.main.nearClipPlane;
        eventDetector = cam.GetComponent<EventDetector>();
    }
 
    private void Update() { // 更新场景(Ctrl+Scroll: 缩放场景, Ctrl+Drag: 平移场景, Alt+Drag: 旋转场景)
        if (eventDetector.HasCtrlScrollEvent()) { // 缩放场景
            ScaleScene(eventDetector.Scroll());
        } else if (eventDetector.IsBeginDrag()) {
            preMousePos = Input.mousePosition;
        } else if (eventDetector.HasDragEvent()) {
            Vector3 offset = Input.mousePosition - preMousePos;
            if (eventDetector.EventType() == MyEventType.CtrlDrag) { // 移动场景
                MoveScene(offset);
            } else if (eventDetector.EventType() == MyEventType.AltDrag) { // 旋转场景
                RotateScene(offset);
            }
            preMousePos = Input.mousePosition;
        }
    }
 
    private void ScaleScene(float scroll) { // 缩放场景
        cam.position += cam.forward * scroll;
        camChangedHandler?.Invoke();
    }
 
    private void MoveScene(Vector3 offset) { // 平移场景
        cam.position -= (cam.right * offset.x / 100 + cam.up * offset.y / 100);
        camChangedHandler?.Invoke();
    }
 
    private void RotateScene(Vector3 offset) { // 旋转场景
        Vector3 rotateCenter = GetRotateCenter(0);
        cam.RotateAround(rotateCenter, Vector3.up, offset.x / 3); // 水平拖拽分量
        cam.LookAt(rotateCenter);
        cam.RotateAround(rotateCenter, -cam.right, offset.y / 5); // 竖直拖拽分量
        camChangedHandler?.Invoke();
    }
 
    private Vector3 GetRotateCenter(float planeY) { // 获取旋转中心
        if (Mathf.Abs(cam.forward.y) < Vector3.kEpsilon || Mathf.Abs(cam.position.y) < float.Epsilon)
        {
            return cam.position + cam.forward * (nearPlan + 1 / nearPlan);
        }
        float t = (planeY - cam.position.y) / cam.forward.y;
        float x = cam.position.x + t * cam.forward.x;
        float z = cam.position.z + t * cam.forward.z;
        return new Vector3(x, planeY, z);
    }
}

        SceneController 脚本组件挂在相机下,用于平移、旋转、缩放场景,其原理见→缩放、平移、旋转场景。

3 运行效果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/162983.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python与C++语法比较--字符串篇

tags: C Python 写在前面 刷lc从Python转向C, 不只是语法层面, 还要改变很多的API, 这次记录一下C和Python在字符串方面的一些区别, 供参考. 基本区别 Python字符串是不可变类型, 而C的String为容器(本质上是一个类的别名, 说容器有点不合适, 因为容器内的元素类型已经被指…

Kotlin中的Lambda编程

文章目录1.集合的创建与遍历2.集合的函数式API3.Java函数式API的使用1.集合的创建与遍历 传统意义上的集合主要是List和Set&#xff0c;再广泛一点的话&#xff0c;像Map这样的键值对数据结构也可以包含进来。List&#xff0c;Set和Map再Java中都是接口&#xff0c;List主要的…

Java设计模式-命令模式Command

介绍 命令模式&#xff08;Command Pattern&#xff09;&#xff1a;在软件设计中&#xff0c;我们经常需要向某些对象发送请求&#xff0c;但是并不知道请求的接收 者是谁&#xff0c;也不知道被请求的操作是哪个&#xff0c; 我们只需在程序运行时指定具体的请求接收者即可&…

数影周报:TikTok因在线跟踪被罚500万欧,Windows 7退出历史舞台

本周看点&#xff1a;TikTok因在线跟踪被法国罚款500万欧元 &#xff1b;思科已裁员近700 人&#xff1b;Windows 7退出历史舞台&#xff1b;亚马逊向所有卖家开放Buy with Prime服务&#xff1b;“全路程”完成2亿元C轮融资......数据安全那些事TikTok因在线跟踪被法国罚款500…

Android13 wifi无线调试adb连接设置

在进行adb调试的时候&#xff0c;有时候需要使用wifi连接&#xff0c;或者wifi连接较为方便&#xff0c;早些的Android上&#xff0c;需要设置端口等操作&#xff0c;adb tcpip 6666参考android wifi adb 调试 - 简书 (jianshu.com)好几步操作&#xff0c;在Android13上&#x…

Deque 的理解 STL中stack与queue为什么选择使用deque为底层模板容器

目录 一、Deque的引入 二、Deque是什么&#xff1f; 三、deque的遍历方式&#xff1f;deque的缺陷&#xff1f; 四、它为什么能更贴合与stack与queue&#xff1f; 五、STL中vector与list的底层实现 一、Deque的引入 Stack、Queue在之前的博客中我也是分别使用了更容易处理…

【蓝桥杯】历届真题 杨辉三角形 (省赛)Java

【问题描述】 下面的图形是著名的杨辉三角形: 如果我们按从上到下、从左到右的顺序把所有数排成一列&#xff0c;可以得到如下数列: 1,1&#xff0c;1&#xff0c;1&#xff0c;2,1&#xff0c;1&#xff0c;3,3&#xff0c;1&#xff0c;1,4&#xff0c;6,4&#xff0c;1&…

数字IC设计、验证、FPGA笔试必会 - Verilog经典习题 (六)多功能数据处理器

数字IC设计、验证、FPGA笔试必会 - Verilog经典习题 &#xff08;六&#xff09;多功能数据处理器 &#x1f508;声明&#xff1a; &#x1f603;博主主页&#xff1a;王_嘻嘻的CSDN博客 &#x1f9e8;未经作者允许&#xff0c;禁止转载 &#x1f511;系列专栏&#xff1a;牛客…

react基础Day02-受控组件评论案例propscontext

React组件 目标 能够知道受控组件是什么能够写出受控组件了解非受控组件 表单处理 受控组件&#xff08;★★★&#xff09; HTML中的表单元素是可输入的&#xff0c;也就是有自己的可变状态而React中可变状态通常保存在state中&#xff0c;并且只能通过setState() 方法来…

[acwing周赛复盘] 第 86 场周赛20230114

[acwing周赛复盘] 第 86 场周赛20230114 一、本周周赛总结二、 4794. 健身1. 题目描述2. 思路分析3. 代码实现三、4795. 安全区域1. 题目描述2. 思路分析3. 代码实现四、4796. 删除序列1. 题目描述2. 思路分析3. 代码实现六、参考链接一、本周周赛总结 去吃羊蝎子了&#xff0…

基于汽车知识图谱的汽车问答多轮对话系统 详细教程

结果: 1 技术路线 该技术路线主要将KBQA分为三部分,实体识别与实体链接,关系识别,sparql查询,其中每个部分分为一到多种方法实现。具体的处理流程图如下:

大脑的记忆

AI神经网络中的记忆 当前AI发展进入一个瓶颈,大家都意识到还是要继续在人脑中获取AI方向的指引。当然也有科学家说物理世界与心理世界并非一一对应,人类的智能也没必要与物理世界一一对应,甚至本质上都可以是不同的,所以没必要研究大脑认知和大脑的机制,更不需要分子级别…

IDEA structure窗口各标志及功能

文章目录图标对象类型访问权限其他修饰符工具栏图标 对象类型 class 类 interface 接口 enum 枚举 interface 注解 class initializer 代码块 method 方法 field 字段/属性 anonymous class 匿名类 lambda lambda表达式 propertie 访问器&#xff08;get方法&#xff0…

【Java面试】Queue接口

文章目录BlockingQueue中有哪些方法&#xff0c;为什么这样设计&#xff1f;BlockingQueue是怎么实现的&#xff1f;BlockingQueue中有哪些方法&#xff0c;为什么这样设计&#xff1f; 先看一眼结构&#xff0c;再看具体的分析 为了应对不同的业务场景&#xff0c;Blockin…

拉伯证券|业绩猛增超13倍,主力连续抢筹,这只股收获4连板

成绩陡增股获主力接连抢筹 春节日益接近&#xff0c;A股成交活跃度有所下滑&#xff0c;不过有一些股票节前继续取得主力喜爱。证券时报•数据宝核算&#xff0c;到1月12日收盘&#xff0c;沪深两市共54只个股接连5日或5日以上主力资金净流入。 主力资金净流入继续周期最长的是…

人工智能学习07--pytorch03--tensorboard(下载tensorboard、opencv)

transform 主要是对input图像进行变换&#xff08;统一尺寸、对图像中的数据进行类的转换&#xff09; TensorBoard很有用 如&#xff1a;通过loss的变化过程&#xff0c;来看loss的变化是否复合预想。也可以通过loss来选择模型。 TensorBoard&#xff0c;虽然他是TensorFlo…

排序综合(C++版)

目录 排序综合 一、问题描述 二、运行环境说明 三、代码段 四、效果展示 排序综合 备注&#xff1a;大二&#xff08;上&#xff09;数据结构课程设计B题 一、问题描述 给定N…

Python asyncio异步编程简单实现

今天继续给大家介绍Python相关知识&#xff0c;本文主要内容是Python asyncio异步编程简单实现。 一、asyncio事件循环简介 asyncio引入了事件循环的概念。事件循环是一个死循环&#xff0c;还循环会检测并执行某些代码。在Python中&#xff0c;引入了asyncio模块后&#xff…

动态内存管理:学习笔记9

目录 一.前言 二.动态内存函数 1.malloc和free 2.calloc函数 3. realloc函数(动态内存空间调整函数) 情形一&#xff1a;扩容时&#xff0c;原内存地址处可以容纳调整后的动态内存 情形二&#xff1a;扩容时&#xff0c;原内存地址无法容纳调整后的动态内存 三.C/C程序…

MATLAB实现费诺编码的计算与分析

一、实验目的 1、理解霍费诺编码的原理。 2、掌握费诺编码的方法和步骤。 3、熟悉费诺编码的效率。 4、本实验用Matlab语言编程实现费诺&#xff08;Fano&#xff09;编码。 二、实验环境 windows XP&#xff0c;MATLAB 7 三、实验原理 费诺编码算法如下&#xff1a;在信源…