【Filament】绘制矩形

news2024/11/26 9:36:07

1 前言

        Filament环境搭建中介绍了 Filament 的 Windows 和 Android 环境搭,绘制三角形中介绍了绘制纯色和彩色三角形,本文将使用 Filament 绘制纯色和彩色矩形。

2 绘制矩形

        本文项目结构如下,完整代码资源 → Filament绘制矩形。

2.1 自定义基类

        为方便读者将注意力聚焦在 Filament 的输入上,轻松配置复杂的环境依赖逻辑,笔者仿照 OpenGL ES 的写法,抽出了 FLSurfaceView 和 BaseModel 类。FLSurfaceView 与 GLSurfaceView 的功能类似,承载了渲染环境配置;BaseModel 中提供了一些 VertexBuffer、IndexBuffer、Material、Renderable 相关的工具类,方便子类直接使用这些工具类。

        build.gradle

...
android {
    ...
    aaptOptions { // 在应用程序打包过程中不压缩的文件
        noCompress 'filamat', 'ktx'
    }
}
 
dependencies {
    implementation fileTree(dir: '../libs', include: ['*.aar'])
    ...
}

        说明:在项目根目录下的 libs 目录中,需要放入以下 aar 文件,它们源自Filament环境搭建中编译生成的 aar。

        FLSurfaceView.java

package com.zhyan8.square.filament;

import android.content.Context;
import android.graphics.Point;
import android.view.Choreographer;
import android.view.Surface;
import android.view.SurfaceView;

import com.google.android.filament.Camera;
import com.google.android.filament.Engine;
import com.google.android.filament.EntityManager;
import com.google.android.filament.Filament;
import com.google.android.filament.Renderer;
import com.google.android.filament.Scene;
import com.google.android.filament.Skybox;
import com.google.android.filament.SwapChain;
import com.google.android.filament.View;
import com.google.android.filament.Viewport;
import com.google.android.filament.android.DisplayHelper;
import com.google.android.filament.android.FilamentHelper;
import com.google.android.filament.android.UiHelper;

/*
 * Filament中待渲染的SurfaceView
 * 功能可以类比OpenGL ES中的GLSurfaceView
 * 用于创建Filament的渲染环境
 */
public class FLSurfaceView extends SurfaceView {
    public static int RENDERMODE_WHEN_DIRTY = 0; // 用户请求渲染才渲染一帧
    public static int RENDERMODE_CONTINUOUSLY = 1; // 持续渲染
    protected int mRenderMode = RENDERMODE_CONTINUOUSLY; // 渲染模式
    protected Choreographer mChoreographer; // 消息控制
    protected DisplayHelper mDisplayHelper; // 管理Display(可以监听分辨率或刷新率的变化)
    protected UiHelper mUiHelper; // 管理SurfaceView、TextureView、SurfaceHolder
    protected Engine mEngine; // 引擎(跟踪用户创建的资源, 管理渲染线程和硬件渲染器)
    protected Renderer mRenderer; // 渲染器(用于操作系统窗口, 生成绘制命令, 管理帧延时)
    protected Scene mScene; // 场景(管理渲染对象、灯光)
    protected View mView; // 存储渲染数据(View是Renderer操作的对象)
    protected Camera mCamera; // 相机(视角管理)
    protected Point mDesiredSize; // 渲染分辨率
    protected float[] mSkyboxColor; // 背景颜色
    protected SwapChain mSwapChain; // 操作系统的本地可渲染表面(native renderable surface, 通常是一个window或view)
    protected FrameCallback mFrameCallback = new FrameCallback(); // 帧回调

    static {
        Filament.init();
    }

    public FLSurfaceView(Context context) {
        super(context);
        mChoreographer = Choreographer.getInstance();
        mDisplayHelper = new DisplayHelper(context);
    }

    public void init() { // 初始化
        setupSurfaceView();
        setupFilament();
        setupView();
        setupScene();
    }

    public void setRenderMode(int renderMode) { // 设置渲染模式
        mRenderMode = renderMode;
    }

    public void requestRender() { // 请求渲染
        mChoreographer.postFrameCallback(mFrameCallback);
    }

    public void onResume() { // 恢复
        mChoreographer.postFrameCallback(mFrameCallback);
    }

    public void onPause() { // 暂停
        mChoreographer.removeFrameCallback(mFrameCallback);
    }

    public void onDestroy() { // 销毁Filament环境
        mChoreographer.removeFrameCallback(mFrameCallback);
        mUiHelper.detach();
        mEngine.destroyRenderer(mRenderer);
        mEngine.destroyView(mView);
        mEngine.destroyScene(mScene);
        mEngine.destroyCameraComponent(mCamera.getEntity());
        EntityManager entityManager = EntityManager.get();
        entityManager.destroy(mCamera.getEntity());
        mEngine.destroy();
    }

    protected void setupScene() { // 设置Scene参数
    }

    protected void onResized(int width, int height) { // Surface尺寸变化时回调
        double zoom = 1;
        double aspect = (double) width / (double) height;
        mCamera.setProjection(Camera.Projection.ORTHO,
                -aspect * zoom, aspect * zoom, -zoom, zoom, 0, 1000);
    }

    private void setupSurfaceView() { // 设置SurfaceView
        mUiHelper = new UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK);
        mUiHelper.setRenderCallback(new SurfaceCallback());
        if (mDesiredSize != null) {
            mUiHelper.setDesiredSize(mDesiredSize.x, mDesiredSize.y);
        }
        mUiHelper.attachTo(this);
    }

    private void setupFilament() { // 设置Filament参数
        mEngine = Engine.create();
        // mEngine = (new Engine.Builder()).featureLevel(Engine.FeatureLevel.FEATURE_LEVEL_0).build();
        mRenderer = mEngine.createRenderer();
        mScene = mEngine.createScene();
        mView = mEngine.createView();
        mCamera = mEngine.createCamera(mEngine.getEntityManager().create());
    }

    private void setupView() { // 设置View参数
        float[] color = mSkyboxColor != null ? mSkyboxColor : new float[] {0, 0, 0, 1};
        Skybox skybox = (new Skybox.Builder()).color(color).build(mEngine);
        mScene.setSkybox(skybox);
        if (mEngine.getActiveFeatureLevel() == Engine.FeatureLevel.FEATURE_LEVEL_0) {
            mView.setPostProcessingEnabled(false); // FEATURE_LEVEL_0不支持post-processing
        }
        mView.setCamera(mCamera);
        mView.setScene(mScene);
    }

    /*
     * 帧回调
     */
    private class FrameCallback implements Choreographer.FrameCallback {
        @Override
        public void doFrame(long frameTimeNanos) { // 渲染每帧数据
            if (mRenderMode == RENDERMODE_CONTINUOUSLY) {
                mChoreographer.postFrameCallback(this); // 请求下一帧
            }
            if (mUiHelper.isReadyToRender()) {
                if (mRenderer.beginFrame(mSwapChain, frameTimeNanos)) {
                    mRenderer.render(mView);
                    mRenderer.endFrame();
                }
            }
        }
    }

    /*
     * Surface回调
     */
    private class SurfaceCallback implements UiHelper.RendererCallback {
        @Override
        public void onNativeWindowChanged(Surface surface) { // Native窗口改变时回调
            if (mSwapChain != null) {
                mEngine.destroySwapChain(mSwapChain);
            }
            long flags = mUiHelper.getSwapChainFlags();
            if (mEngine.getActiveFeatureLevel() == Engine.FeatureLevel.FEATURE_LEVEL_0) {
                if (SwapChain.isSRGBSwapChainSupported(mEngine)) {
                    flags = flags | SwapChain.CONFIG_SRGB_COLORSPACE;
                }
            }
            mSwapChain = mEngine.createSwapChain(surface, flags);
            mDisplayHelper.attach(mRenderer, getDisplay());
        }

        @Override
        public void onDetachedFromSurface() { // 解绑Surface时回调
            mDisplayHelper.detach();
            if (mSwapChain != null) {
                mEngine.destroySwapChain(mSwapChain);
                mEngine.flushAndWait();
                mSwapChain = null;
            }
        }

        @Override
        public void onResized(int width, int height) { // Surface尺寸变化时回调
            mView.setViewport(new Viewport(0, 0, width, height));
            FilamentHelper.synchronizePendingFrames(mEngine);
            FLSurfaceView.this.onResized(width, height);
        }
    }
}

        BaseModel.java

package com.zhyan8.square.filament;

import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import com.google.android.filament.Box;
import com.google.android.filament.Engine;
import com.google.android.filament.EntityManager;
import com.google.android.filament.IndexBuffer;
import com.google.android.filament.Material;
import com.google.android.filament.RenderableManager;
import com.google.android.filament.RenderableManager.PrimitiveType;
import com.google.android.filament.VertexBuffer;
import com.google.android.filament.VertexBuffer.AttributeType;
import com.google.android.filament.VertexBuffer.VertexAttribute;

import java.io.FileInputStream;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;

/*
 * 模型基类
 * 管理模型的材质、顶点属性、顶点索引、渲染id
 */
public class BaseModel {
    private static String TAG = "BaseModel";
    protected AssetManager mAssetManager; // 资源管理器
    protected Engine mEngine; // Filament引擎
    protected Material mMaterial; // 模型材质
    protected VertexBuffer mVertexBuffer; // 顶点属性缓存
    protected IndexBuffer mIndexBuffer; // 顶点索引缓存
    protected int mRenderable; // 渲染id
    protected Box mBox; // 渲染区域

    public BaseModel(AssetManager assetManager, Engine engine) {
        mAssetManager = assetManager;
        mEngine = engine;
    }

    public Material getMaterial() { // 获取材质
        return mMaterial;
    }

    public VertexBuffer getVertexBuffer() { // 获取顶点属性缓存
        return mVertexBuffer;
    }

    public IndexBuffer getIndexBuffer() { // 获取顶点索引缓存
        return mIndexBuffer;
    }

    public int getRenderable() { // 获取渲染id
        return mRenderable;
    }

    public void destroy() { // 销毁模型
        mEngine.destroyEntity(mRenderable);
        mEngine.destroyVertexBuffer(mVertexBuffer);
        mEngine.destroyIndexBuffer(mIndexBuffer);
        mEngine.destroyMaterial(mMaterial);
        EntityManager entityManager = EntityManager.get();
        entityManager.destroy(mRenderable);
    }

    protected Material loadMaterial(String materialPath) { // 加载材质
        Buffer buffer = readUncompressedAsset(mAssetManager, materialPath);
        if (buffer != null) {
            Material material = (new Material.Builder()).payload(buffer, buffer.remaining()).build(mEngine);
            material.compile(
                    Material.CompilerPriorityQueue.HIGH,
                    Material.UserVariantFilterBit.ALL,
                    new Handler(Looper.getMainLooper()),
                    () -> Log.i(TAG, "Material " + material.getName() + " compiled."));
            mEngine.flush();
            return material;
        }
        return null;
    }

    protected VertexBuffer getVertexBuffer(float[] values) { // 获取顶点属性缓存
        ByteBuffer vertexData = getByteBuffer(values);
        int vertexCount = values.length / 3;
        int vertexSize = Float.BYTES * 3;
        VertexBuffer vertexBuffer = new VertexBuffer.Builder()
                .bufferCount(1)
                .vertexCount(vertexCount)
                .attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT3, 0, vertexSize)
                .build(mEngine);
        vertexBuffer.setBufferAt(mEngine, 0, vertexData);
        return vertexBuffer;
    }

    protected VertexBuffer getVertexBuffer(Vertex[] values) { // 获取顶点属性缓存
        ByteBuffer vertexData = getByteBuffer(values);
        int vertexCount = values.length;
        int vertexSize = Vertex.BYTES;
        VertexBuffer vertexBuffer = new VertexBuffer.Builder()
                .bufferCount(1)
                .vertexCount(vertexCount)
                .attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT3, 0, vertexSize)
                .attribute(VertexAttribute.COLOR,    0, AttributeType.UBYTE4, 3 * Float.BYTES, vertexSize)
                .normalized(VertexAttribute.COLOR)
                .build(mEngine);
        vertexBuffer.setBufferAt(mEngine, 0, vertexData);
        return vertexBuffer;
    }

    protected IndexBuffer getIndexBuffer(short[] values) { // 获取顶点索引缓存
        ByteBuffer indexData = getByteBuffer(values);
        int indexCount = values.length;
        IndexBuffer indexBuffer = new IndexBuffer.Builder()
                .indexCount(indexCount)
                .bufferType(IndexBuffer.Builder.IndexType.USHORT)
                .build(mEngine);
        indexBuffer.setBuffer(mEngine, indexData);
        return indexBuffer;
    }

    protected int getRenderable(PrimitiveType primitiveType, int vertexCount) { // 获取渲染id
        int renderable = EntityManager.get().create();
        new RenderableManager.Builder(1)
                .boundingBox(mBox)
                .geometry(0, primitiveType, mVertexBuffer, mIndexBuffer, 0, vertexCount)
                .material(0, mMaterial.getDefaultInstance())
                .build(mEngine, renderable);
        return renderable;
    }

    private Buffer readUncompressedAsset(AssetManager assetManager, String assetName) { // 加载资源
        ReadableByteChannel src = null;
        ByteBuffer dist = null;
        try {
            AssetFileDescriptor fd = assetManager.openFd(assetName);
            FileInputStream fis = fd.createInputStream();
            dist = ByteBuffer.allocate((int) fd.getLength());
            src = Channels.newChannel(fis);
            src.read(dist);
        } catch (IOException e) {
            Log.d(TAG, "readUncompressedAsset-catch, e=" + e);
        } finally {
            if (src != null) {
                try {
                    src.close();
                } catch (IOException e) {
                    Log.d(TAG, "readUncompressedAsset-finally, e=" + e);
                }
            }
        }
        if (dist != null) {
            return dist.rewind();
        }
        return null;
    }

    private ByteBuffer getByteBuffer(float[] values) { // float数组转换为ByteBuffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(values.length * Float.BYTES);
        byteBuffer.order(ByteOrder.nativeOrder());
        for (int i = 0; i < values.length; i++) {
            byteBuffer.putFloat(values[i]);
        }
        byteBuffer.flip();
        return byteBuffer;
    }

    private ByteBuffer getByteBuffer(short[] values) { // short数组转换为ByteBuffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(values.length * Short.BYTES);
        byteBuffer.order(ByteOrder.nativeOrder());
        for (int i = 0; i < values.length; i++) {
            byteBuffer.putShort(values[i]);
        }
        byteBuffer.flip();
        return byteBuffer;
    }

    private ByteBuffer getByteBuffer(Vertex[] values) { // Vertex数组转换为ByteBuffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(values.length * Vertex.BYTES);
        byteBuffer.order(ByteOrder.nativeOrder());
        for (int i = 0; i < values.length; i++) {
            values[i].put(byteBuffer);
        }
        byteBuffer.flip();
        return byteBuffer;
    }

    /*
     * 顶点数据
     * 包含顶点位置和颜色
     */
    public static class Vertex {
        public static int BYTES = 16;
        public float x;
        public float y;
        public float z;
        public int color;
        public Vertex() {}
        public Vertex(float x, float y, float z, int color) {
            this.x = x;
            this.y = y;
            this.z = z;
            this.color = color;
        }

        public ByteBuffer put(ByteBuffer buffer) { // Vertex转换为ByteBuffer
            buffer.putFloat(x);
            buffer.putFloat(y);
            buffer.putFloat(z);
            buffer.putInt(color);
            return buffer;
        }
    }
}

2.2 绘制纯色矩形

        MainActivity.java

package com.zhyan8.square;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

import com.zhyan8.square.filament.FLSurfaceView;

public class MainActivity extends AppCompatActivity {
    private FLSurfaceView mFLSurfaceView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mFLSurfaceView = new MyFLSurfaceView(this);
        setContentView(mFLSurfaceView);
        mFLSurfaceView.init();
        mFLSurfaceView.setRenderMode(FLSurfaceView.RENDERMODE_CONTINUOUSLY);
    }

    @Override
    public void onResume() {
        super.onResume();
        mFLSurfaceView.onResume();
    }

    @Override
    public void onPause() {
        super.onPause();
        mFLSurfaceView.onPause();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mFLSurfaceView.onDestroy();
    }
}

        MyFLSurfaceView.java

package com.zhyan8.square;

import android.content.Context;

import com.google.android.filament.Camera;
import com.zhyan8.square.filament.BaseModel;
import com.zhyan8.square.filament.FLSurfaceView;

public class MyFLSurfaceView extends FLSurfaceView {
    private BaseModel mMyModel;
    public MyFLSurfaceView(Context context) {
        super(context);
    }

    public void init() {
        mSkyboxColor = new float[] {0.965f, 0.941f, 0.887f, 1};
        super.init();
    }

    @Override
    public void onDestroy() {
        mMyModel.destroy();
        super.onDestroy();
    }

    @Override
    protected void setupScene() { // 设置Scene参数
        mMyModel = new Square1(getContext().getAssets(), mEngine);
        mScene.addEntity(mMyModel.getRenderable());
    }

    @Override
    protected void onResized(int width, int height) {
        double zoom = 1.5;
        double aspect = (double) width / (double) height;
        mCamera.setProjection(Camera.Projection.ORTHO,
                -aspect * zoom, aspect * zoom, -zoom, zoom, 0, 10);
    }
}

        Square1.java

package com.zhyan8.square;

import android.content.res.AssetManager;

import com.google.android.filament.Box;
import com.google.android.filament.Engine;
import com.google.android.filament.RenderableManager.PrimitiveType;
import com.zhyan8.square.filament.BaseModel;

public class Square1 extends BaseModel {
    private String materialPath = "materials/square1.filamat";
    private float[] mVertices = new float[] {
            -0.5f, -0.5f, 0.0f, // 左下
            0.5f, -0.5f, 0.0f, // 右下
            0.5f, 0.5f, 0.0f, // 右上
            -0.5f, 0.5f, 0.0f // 左上
    };
    private short[] mIndex = new short[] {0, 1, 2, 0, 2, 3};

    public Square1(AssetManager assetManager, Engine engine) {
        super(assetManager, engine);
        init();
    }

    private void init() {
        mBox = new Box(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.01f);
        mMaterial = loadMaterial(materialPath);
        mVertexBuffer = getVertexBuffer(mVertices);
        mIndexBuffer = getIndexBuffer(mIndex);
        mRenderable = getRenderable(PrimitiveType.TRIANGLES, mIndex.length);
    }
}

        square1.mat

material {
    name : square,

    // 禁用所有lighting
    shadingModel : unlit,
    featureLevel : 0
}

fragment {
    void material(inout MaterialInputs material) {
        prepareMaterial(material); // 在方法返回前必须回调该函数
        material.baseColor = vec4(1, 0, 0, 1);
    }
}

        说明:mat 文件需要使用通过 matc.exe 工具转换为 filamat 文件。

        transform.bat

@echo off
setlocal enabledelayedexpansion
set "srcFolder=../src/main/materials"
set "distFolder=../src/main/assets/materials"

for %%f in ("%srcFolder%\*.mat") do (
	set "matfile=%%~nf"
	matc -p mobile -a opengl -o "!matfile!.filamat" "%%f"
    move "!matfile!.filamat" "%distFolder%\!matfile!.filamat"
)

echo Processing complete.
pause

        说明:需要将 matc.exe 文件与 transform.bat 文件放在同一个目录下面,matc.exe 源自Filament环境搭建中编译生成的 exe 文件。双击 transform.bat 文件,会自动将 /src/main/materials/ 下面的所有 mat 文件全部转换为 filamat 文件,并复制到 /src/main/assets/materials/ 文件下面。

        运行效果如下。

2.3 绘制彩色矩形

        MyFLSurfaceView.java

package com.zhyan8.square;

import android.content.Context;

import com.google.android.filament.Camera;
import com.zhyan8.square.filament.BaseModel;
import com.zhyan8.square.filament.FLSurfaceView;

public class MyFLSurfaceView extends FLSurfaceView {
    private BaseModel mMyModel;
    public MyFLSurfaceView(Context context) {
        super(context);
    }

    public void init() {
        mSkyboxColor = new float[] {0.965f, 0.941f, 0.887f, 1};
        super.init();
    }

    @Override
    public void onDestroy() {
        mMyModel.destroy();
        super.onDestroy();
    }

    @Override
    protected void setupScene() { // 设置Scene参数
        mMyModel = new Square2(getContext().getAssets(), mEngine);
        mScene.addEntity(mMyModel.getRenderable());
    }

    @Override
    protected void onResized(int width, int height) {
        double zoom = 1.5;
        double aspect = (double) width / (double) height;
        mCamera.setProjection(Camera.Projection.ORTHO,
                -aspect * zoom, aspect * zoom, -zoom, zoom, 0, 10);
    }
}

        Square2.java

package com.zhyan8.square;

import android.content.res.AssetManager;

import com.google.android.filament.Box;
import com.google.android.filament.Engine;
import com.google.android.filament.RenderableManager.PrimitiveType;
import com.zhyan8.square.filament.BaseModel;

public class Square2 extends BaseModel {
    private String materialPath = "materials/square2.filamat";
    private Vertex[] mVertices = new Vertex[] {
            new Vertex(-0.5f, -0.5f, 0f, 0xffff0000), // 左下
            new Vertex(0.5f, -0.5f, 0f, 0xff00ff00), // 右下
            new Vertex(0.5f, 0.5f, 0f, 0xff0000ff), // 右上
            new Vertex(-0.5f, 0.5f, 0f, 0x000f0f0f), // 左上
    };

    private short[] mIndex = new short[] {0, 1, 2, 0, 2, 3};

    public Square2(AssetManager assetManager, Engine engine) {
        super(assetManager, engine);
        init();
    }

    private void init() {
        mBox = new Box(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.01f);
        mMaterial = loadMaterial(materialPath);
        mVertexBuffer = getVertexBuffer(mVertices);
        mIndexBuffer = getIndexBuffer(mIndex);
        mRenderable = getRenderable(PrimitiveType.TRIANGLES, mIndex.length);
    }
}

        square2.mat

material {
    name : square,

    // 顶点着色器入参MaterialVertexInputs中需要的顶点属性
    requires : [
        color
    ],

    // 禁用所有lighting
    shadingModel : unlit,
    featureLevel : 0
}

fragment {
    void material(inout MaterialInputs material) {
        prepareMaterial(material); // 在方法返回前必须回调该函数
        material.baseColor = getColor();
    }
}

        运行效果如下。 

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

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

相关文章

【Matlab算法】灰狼优化算法问题(Grey Wolf Optimization)(附MATLAB完整代码)

灰狼优化算法问题 前言算法描述&#xff1a;算法特点&#xff1a; 正文代码实现 前言 灰狼优化算法&#xff08;Grey Wolf Optimization&#xff0c;GWO&#xff09; 是一种模拟灰狼社会行为的启发式优化算法。它是由Seyedali Mirjalili等人于2014年提出的&#xff0c;灵感来源…

Acrel-1000DP分布式光伏系统在某重工企业18MW分布式光伏中应用——安科瑞 顾烊宇

摘 要&#xff1a;分布式光伏发电特指在用户场地附近建设&#xff0c;运行方式以用户侧自发自用、余电上网&#xff0c;且在配电系统平衡调节为特征的光伏发电设施&#xff0c;是一种新型的、具有广阔发展前景的发电和能源综合利用方式&#xff0c;它倡导就近发电&#xff0c;就…

React实现全局Loading

css #__loading {position:fixed;top: 0;left: 0;z-index: 99999;display: flex;align-items: center;justify-content: center;width: 100%;height: 100%;background: rgba(0, 0, 0, 0); } 页面代码 使用了antd的Spin组件 import React from react import ReactDOM from re…

C语言入门(字符串正反连接(fgets()、gets()用法))

目录 ​编辑 题目描述 输入格式 输出格式 样例输入 样例输出 解题思路 题目描述 所给字符串正序和反序连接&#xff0c;形成新串并输出 输入格式 任意字符串&#xff08;长度<50&#xff09; 输出格式 字符串正序和反序连接所成的新字符串 样例输入 123abc 样例输出 12…

2023.12.18杂记

今天特地搜了一下国内不错的博客网站&#xff0c;本来想在掘金上写的&#xff0c;但是怕被人喷&#xff08;&#xff0c;所以还是决定在csdn上写了哈哈哈。 这篇文章主要整理一下我今天写代码时遇到的疑惑以及记录一下思考过程吧。 第一个注意的地方&#xff0c;我们的get查询…

前端开发中的webpack打包工具

前端技术发展迅猛&#xff0c;各种可以提高开发效率的新思想和框架层出不穷&#xff0c;但是它们都有一个共同点&#xff0c;即源代码无法直接运行&#xff0c;必须通过转换后才可以正常运行。webpack是目前主流的打包模块化JavaScript的工具之一。 本章主要涉及的知识点有&am…

【PHP入门】2.1-运算符

-运算符- 运算符&#xff1a;operator&#xff0c;是一种将数据进行运算的特殊符号&#xff0c;在PHP中一共有十种运算符之多。 2.1.1赋值运算符 赋值运算&#xff1a;符号是“”&#xff0c;表示将右边的结果&#xff08;可以是变量、数据、常量和其它运算出来的结果&#…

音视频学习(二十一)——rtmp收流(tcp方式)

前言 本文主要介绍rtmp协议收流流程&#xff0c;在linux上搭建rtmp服务器&#xff0c;通过自研的rtmp收流库发起取流请求&#xff0c;使用ffmpegqt实现视频流的解码与播放。 关于rtmp协议基础介绍可查看&#xff1a;https://blog.csdn.net/www_dong/article/details/13102607…

sqlserver dba日常操作

查询慢sql的方法 1.whoisactive 安装方法 http://whoisactive.com/downloads/下载地址 将下载好的zip包放到sqlserver服务器中 文件-打开-文件-下载好的zip包-在查询窗口点击执行 新建一个查询窗口&#xff0c;输入sp_whoisactive&#xff0c;获取当前运行的所有sql语句 使用…

windows 安装jenkins

下载jenkins 官方下载地址&#xff1a;Jenkins 的安装和设置 清华源下载地址&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/jenkins/windows-stable/ 最新支持java8的版本时2.346.1版本&#xff0c;在清华源中找不到&#xff0c;在官网中没找到windows的下载历史&#xff…

ELK(九)—logstash

目录 简介安装部署测试 配置详解编写配置文件连接elasticsearch将数据推送到elasticsearch中。 简介 Logstash 是一个开源的服务器端数据处理管道&#xff0c;由 Elastic 公司维护和开发。它被设计用于从不同来源收集、处理和转发数据&#xff0c;以供 Elasticsearch 进行存储…

Mysql主从复制,读写分离,分表分库策略与实践

本文转自互联网&#xff0c;本文部分内容来源于网络&#xff0c;为了把本文主题讲得清晰透彻&#xff0c;也整合了很多我认为不错的技术博客内容&#xff0c;引用其中了一些比较好的博客文章&#xff0c;如有侵权&#xff0c;请联系作者。 该系列博文会告诉你如何从入门到进阶&…

docker在线安装minio

1、下载最新minio docker pull minio/minio 2、在宿主机创建 /usr/local/data/miniodocker/config 和 /usr/local/data/miniodocker/data,执行docker命令 docker run -p 9000:9000 -p 9090:9090 --name minio -d --restartalways -e MINIO_ACCESS_KEYminio -e MINIO_SECRET_K…

miRMaker

Introduction 除了miRNA表达数据&#xff0c;各种miRNA相关的知识也强有力地支持了对miRNA功能相互作用的理解。 那些具有许多共同调控靶基因或疾病的miRNAs可能具有相似的功能 一些方法通过考虑实验验证的miRNA-靶标关系来评估miRNA相互作用&#xff0c;评估miRNA功能相互作…

KUKA机器人Loop循环的具体使用方法示例

KUKA机器人Loop循环的具体使用方法示例 如下图所示&#xff0c;新建一个示例程序&#xff0c; 如下图所示&#xff0c;添加一些动作指令&#xff0c; 如下图所示&#xff0c;如果想要机器人在第5行和第9行之间循环执行程序&#xff0c;则可以在第5行添加指令loop&#xff0…

Linux 特殊符号

目录 1. # 注释 2. &#xff1b;命令分隔符 3. .. 上级目录 4. . 当前目录 5. " " 换行&#xff0c;解析变量 6. 换行&#xff0c;不解析变量 7. \ 和 / 8. &#xff01;历史命令调用&#xff0c;取反 9. * 通配符 10. $ 调用变量 11. | 管道 12. || …

AWS-WAF-CDN基于速率rate的永久黑名单方案(基于lambda实现)

参考方案&#xff08;有坑&#xff09;&#xff0c; 所以产生了这篇博客&#xff1a; 点击跳转 1. 部署waf &#xff08;有则跳过&#xff09; 必须存在一个rate速率规则&#xff0c;后面的方案堆栈要用 新建rate速率规则 关联cdn资源 2.部署堆栈 (美国东部 (弗吉尼亚北部 …

安卓小练习-校园闲置交易APP(SQLite+SimpleCursorAdapter适配器)

环境&#xff1a; SDK&#xff1a;34 JDK&#xff1a;20.0.2 编写工具&#xff1a;Android Studio 2022.3.1 整体效果&#xff08;视频演示&#xff09;&#xff1a; 小练习-闲置社区APP演示视频-CSDN直播 部分效果截图&#xff1a; 整体工作流程&#xff1a; 1.用户登录&…

【基础篇】1.2 认识STM32(二)

3.3 VREF/VREF-引脚 VREF和VREF-是STM32中用于提供参考电压的引脚。如下图&#xff1a; VREF引脚可以连接一个单独的外部参考电压&#xff0c;范围在2.0V&#xff5e;VDDA&#xff0c;但不能超过VDDA&#xff0c;否则就超过了模拟器件的最大供电电压。在100引脚的封装中&#…

数据可视化---双Y轴折线图比较

内容导航 类别内容导航机器学习机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归机器学习算法—聚类机器学习算法—异常检测机器学习算法—时间序列数据可视化数据可视化—折线图数据可视化—箱线图数据可视化—柱状图数据可视化—饼图、环形图、雷达图统…