AntV G6新版源码浅析

news2024/11/24 18:56:29

在这里插入图片描述

前言

AntV是蚂蚁金服全新一代数据可视化解决方案,其中G6主要用于解决图可视领域相关的前端可视化问题,其是一个简单、易用、完备的图可视化引擎。本文旨在通过简要分析G6 5.x版本源码来对图可视领域的一些底层引擎进行一个大致了解,同时也为G6引擎的社区共建共享提供一些力量,可以更好的提供插件化功能的编写。

架构

在这里插入图片描述

新版G6整体是基于“插件化”的架构进行设计的,对外整体暴露Graph类及StdLib标准库,将主题、数据处理、布局、视图、类型、交互等均作为插件来进行处理,提供更高层次的定制化需求,提升更好的开源能力。

目录

整体采用monorepo进行源码的仓库管理

  • packages
    • g6
    • docs
    • src
      • constant
        • index.ts
        • shape.ts
      • item
        • combo.ts
        • edge.ts
        • item.ts
        • node.ts
      • runtime
        • controller
          • data.ts
          • extensions.ts
          • index.ts
          • interaction.ts
          • item.ts
          • layout.ts
          • plugin.ts
          • theme.ts
          • viewport.ts
        • graph.ts
        • hooks.ts
      • stdlib
        • behavior
          • activate-relations.ts
          • brush-select.ts
          • click-select.ts
          • drag-canvas.ts
          • drag-node.ts
          • hover-activate.ts
          • lasso-select.ts
          • orbit-canvas-3d.ts
          • rotate-canvas-3d.ts
          • track-canvas-3d.ts
          • zoom-canvas-3d.ts
          • zoom-canvas.ts
        • data
          • comboFromNode.ts
        • item
          • edge
            • base.ts
            • index.ts
            • line.ts
          • node
            • base.ts
            • base3d.ts
            • circle.ts
            • index.ts
            • sphere.ts
        • plugin
          • grid
            • index.ts
          • legend
            • index.ts
          • minimap
            • index.ts
        • selector
          • lasso.ts
          • rect.ts
        • theme
          • dark.ts
          • light.ts
        • themeSolver
          • base.ts
          • spec.ts
          • subject.ts
        • index.ts
      • types
        • animate.ts
        • behavior.ts
        • combo.ts
        • common.ts
        • data.ts
        • edge.ts
        • event.ts
        • graph.ts
        • hook.ts
        • index.ts
        • item.ts
        • layout.ts
        • node.ts
        • plugin.ts
        • render.ts
        • spec.ts
        • stdlib.ts
        • theme.ts
        • view.ts
      • util
        • animate.ts
        • array.ts
        • canvas.ts
        • event.ts
        • extend.ts
        • extension.ts
        • index.ts
        • item.ts
        • mapper.ts
        • math.ts
        • point.ts
        • shape.ts
        • shape3d.ts
        • text.ts
        • type.ts
        • zoom.ts
      • index.ts
    • tests

源码

从架构层次可以看出,整体对外暴露的就是Graph的类以及stdLib的标准库,因而在分析源码调用过程中,我们抓住Graph进行逐步的往外拓展,从而把握整体的一个设计链路,避免陷入局部无法抽离。

Graph

对外暴露的Graph类是整个G6图的核心类

// https://github.com/antvis/G6/blob/v5/packages/g6/src/runtime/graph.ts
export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
  extends EventEmitter
  implements IGraph<B, T>
{
  public hooks: Hooks;
  // for nodes and edges, which will be separate into groups
  public canvas: Canvas;
  // the container dom for the graph canvas
  public container: HTMLElement;
  // the tag to indicate whether the graph instance is destroyed
  public destroyed: boolean;
  // the renderer type of current graph
  public rendererType: RendererName;
  // for transient shapes for interactions, e.g. transient node and related edges while draging, delegates
  public transientCanvas: Canvas;
  // for background shapes, e.g. grid, pipe indices
  public backgroundCanvas: Canvas;
  // the tag indicates all the three canvases are all ready
  private canvasReady: boolean;
  private specification: Specification<B, T>;
  private dataController: DataController;
  private interactionController: InteractionController;
  private layoutController: LayoutController;
  private viewportController: ViewportController;
  private itemController: ItemController;
  private extensionController: ExtensionController;
  private themeController: ThemeController;
  private pluginController: PluginController;

  private defaultSpecification = {
    theme: {
      type: 'spec',
      base: 'light',
    },
  };

  constructor(spec: Specification<B, T>) {
    super();
    // TODO: analyse cfg

    this.specification = Object.assign({}, this.defaultSpecification, spec);
    this.initHooks();
    this.initCanvas();
    this.initControllers();

    this.hooks.init.emit({
      canvases: {
        background: this.backgroundCanvas,
        main: this.canvas,
        transient: this.transientCanvas,
      },
    });

    const { data } = spec;
    if (data) {
      // TODO: handle multiple type data configs
      this.read(data as GraphData);
    }
  }

  // 初始化控制器,用于各种类型插件的依赖注入
  private initControllers() {
    this.dataController = new DataController(this);
    this.interactionController = new InteractionController(this);
    this.layoutController = new LayoutController(this);
    this.themeController = new ThemeController(this);
    this.itemController = new ItemController(this);
    this.viewportController = new ViewportController(this);
    this.extensionController = new ExtensionController(this);
    this.pluginController = new PluginController(this);
  }

  // 初始化画布
  private initCanvas() {
    const { renderer, container, width, height } = this.specification;
    let pixelRatio;
    if (renderer && !isString(renderer)) {
      // @ts-ignore
      this.rendererType = renderer.type || 'canvas';
      // @ts-ignore
      pixelRatio = renderer.pixelRatio;
    } else {
      // @ts-ignore
      this.rendererType = renderer || 'canvas';
    }
    const containerDOM = isString(container)
      ? document.getElementById(container as string)
      : container;
    if (!containerDOM) {
      console.error(
        `Create graph failed. The container for graph ${containerDOM} is not exist.`,
      );
      this.destroy();
      return;
    }
    this.container = containerDOM;
    const size = [width, height];
    if (size[0] === undefined) {
      size[0] = containerDOM.scrollWidth;
    }
    if (size[1] === undefined) {
      size[1] = containerDOM.scrollHeight;
    }

    this.backgroundCanvas = createCanvas(
      this.rendererType,
      containerDOM,
      size[0],
      size[1],
      pixelRatio,
    );
    this.canvas = createCanvas(
      this.rendererType,
      containerDOM,
      size[0],
      size[1],
      pixelRatio,
    );
    this.transientCanvas = createCanvas(
      this.rendererType,
      containerDOM,
      size[0],
      size[1],
      pixelRatio,
      true,
      {
        pointerEvents: 'none',
      },
    );
    Promise.all(
      [this.backgroundCanvas, this.canvas, this.transientCanvas].map(
        (canvas) => canvas.ready,
      ),
    ).then(() => (this.canvasReady = true));
  }

  // 改变渲染类型,默认为Canvas
  public changeRenderer(type) {
    
  }

  // 初始化生命周期钩子函数
  private initHooks() {
    this.hooks = {
      init: new Hook<{
        canvases: {
          background: Canvas;
          main: Canvas;
          transient: Canvas;
        };
      }>({ name: 'init' }),
      datachange: new Hook<{ data: GraphData; type: DataChangeType }>({
        name: 'datachange',
      }),
      itemchange: new Hook<{
        type: ITEM_TYPE;
        changes: GraphChange<NodeModelData, EdgeModelData>[];
        graphCore: GraphCore;
        theme: ThemeSpecification;
      }>({ name: 'itemchange' }),
      render: new Hook<{
        graphCore: GraphCore;
        theme: ThemeSpecification;
        transientCanvas: Canvas;
      }>({
        name: 'render',
      }),
      layout: new Hook<{ graphCore: GraphCore }>({ name: 'layout' }),
      viewportchange: new Hook<ViewportChangeHookParams>({ name: 'viewport' }),
      modechange: new Hook<{ mode: string }>({ name: 'modechange' }),
      behaviorchange: new Hook<{
        action: 'update' | 'add' | 'remove';
        modes: string[];
        behaviors: (string | BehaviorOptionsOf<{}>)[];
      }>({ name: 'behaviorchange' }),
      itemstatechange: new Hook<{
        ids: ID[];
        states?: string[];
        value?: boolean;
      }>({
        name: 'itemstatechange',
      }),
      itemvisibilitychange: new Hook<{ ids: ID[]; value: boolean }>({
        name: 'itemvisibilitychange',
      }),
      transientupdate: new Hook<{
        type: ITEM_TYPE | SHAPE_TYPE;
        id: ID;
        config: {
          style: ShapeStyle;
          action: 'remove' | 'add' | 'update' | undefined;
        };
        canvas: Canvas;
      }>({ name: 'transientupdate' }),
      pluginchange: new Hook<{
        action: 'update' | 'add' | 'remove';
        plugins: (
          | string
          | { key: string; type: string; [cfgName: string]: unknown }
        )[];
      }>({ name: 'pluginchange' }),
      themechange: new Hook<{
        theme: ThemeSpecification;
        canvases: {
          background: Canvas;
          main: Canvas;
          transient: Canvas;
        };
      }>({ name: 'init' }),
      destroy: new Hook<{}>({ name: 'destroy' }),
    };
  }

  // 更改spec配置
  public updateSpecification(spec: Specification<B, T>): Specification<B, T> {
    
  }

  // 更改theme配置
  public updateTheme(theme: ThemeOptionsOf<T>) {
    
  }

  // 获取配置信息
  public getSpecification(): Specification<B, T> {
    
  }

  // 数据渲染,diff比对
  public async read(data: GraphData) {
    
  }

  // 更改图数据
  public async changeData(
    data: GraphData,
    type: 'replace' | 'mergeReplace' = 'mergeReplace',
  ) {
    
  }

  // 清空画布
  public clear() {
    
  }

  // 获取视图中心
  public getViewportCenter(): PointLike {
    
  }
  
  // 更给视图转换
  public async transform(
    options: GraphTransformOptions,
    effectTiming?: CameraAnimationOptions,
  ): Promise<void> {
    
  }

  // 立刻停止当前过渡变化
  public stopTransformTransition() {
    
  }

  // 画布位移
  public async translate(
    distance: Partial<{
      dx: number;
      dy: number;
      dz: number;
    }>,
    effectTiming?: CameraAnimationOptions,
  ) {
    
  }

  // 画布移动至视图坐标
  public async translateTo(
    { x, y }: PointLike,
    effectTiming?: CameraAnimationOptions,
  ) {
    
  }

  // 画布放大/缩小
  public async zoom(
    ratio: number,
    origin?: PointLike,
    effectTiming?: CameraAnimationOptions,
  ) {
    
  }

  // 画布放大/缩小至
  public async zoomTo(
    zoom: number,
    origin?: PointLike,
    effectTiming?: CameraAnimationOptions,
  ) {
    
  }

  // 获取画布放大/缩小比例
  public getZoom() {
    
  }

  // 旋转画布
  public async rotate(
    angle: number,
    origin?: PointLike,
    effectTiming?: CameraAnimationOptions,
  ) {
    
  }

  // 旋转画布至 
  public async rotateTo(
    angle: number,
    origin?: PointLike,
    effectTiming?: CameraAnimationOptions,
  ) {
    
  }

  // 自适应画布
  public async fitView(
    options?: {
      padding: Padding;
      rules: FitViewRules;
    },
    effectTiming?: CameraAnimationOptions,
  ) {
    
  }
  
  // 对齐画布中心与视图中心
  public async fitCenter(effectTiming?: CameraAnimationOptions) {
    
  }
  // 对齐元素
  public async focusItem(id: ID | ID[], effectTiming?: CameraAnimationOptions) {
    
  }

  // 获取画布大小
  public getSize(): number[] {
    
  }

  // 设置画布大小
  public setSize(size: number[]) {
    
  }

  // 获取视图下的渲染坐标
  public getCanvasByViewport(viewportPoint: Point): Point {
    
  }

  // 获取画布下的渲染视图
  public getViewportByCanvas(canvasPoint: Point): Point {
    
  }

  // 获取浏览器坐标
  public getClientByCanvas(canvasPoint: Point): Point {
    
  }

  // 获取画布坐标
  public getCanvasByClient(clientPoint: Point): Point {
    
  }

  // ===== item operations =====
  
  // 获取节点数据
  public getNodeData(condition: ID | Function): NodeModel | undefined {
    
  }
  
  // 获取边数据
  public getEdgeData(condition: ID | Function): EdgeModel | undefined {
    
  }
 
  // 获取combo数据
  public getComboData(condition: ID | Function): ComboModel | undefined {
    
  }
  
  // 获取所有节点数据
  public getAllNodesData(): NodeModel[] {
    
  }
  
  // 获取所有边数据
  public getAllEdgesData(): EdgeModel[] {
    
  }
  
  // 获取所有combo类型数据
  public getAllCombosData(): ComboModel[] {
    
  }
  
  // 获取相关边数据
  public getRelatedEdgesData(
    nodeId: ID,
    direction: 'in' | 'out' | 'both' = 'both',
  ): EdgeModel[] {
    
  }
  
  // 获取临近节点数据
  public getNeighborNodesData(
    nodeId: ID,
    direction: 'in' | 'out' | 'both' = 'both',
  ): NodeModel[] {
    
  }

  // 获取状态类型的id
  public findIdByState(
    itemType: ITEM_TYPE,
    state: string,
    value: string | boolean = true,
    additionalFilter?: (item: NodeModel | EdgeModel | ComboModel) => boolean,
  ): ID[] {
    
  }
  
  // 添加数据
  public addData(
    itemType: ITEM_TYPE,
    models:
      | NodeUserModel
      | EdgeUserModel
      | ComboUserModel
      | NodeUserModel[]
      | EdgeUserModel[]
      | ComboUserModel[],
    stack?: boolean,
  ):
    | NodeModel
    | EdgeModel
    | ComboModel
    | NodeModel[]
    | EdgeModel[]
    | ComboModel[] {
    
  }
  
  // 移除数据
  public removeData(itemType: ITEM_TYPE, ids: ID | ID[], stack?: boolean) {
    
  }
  
  // 更新数据
  public updateData(
    itemType: ITEM_TYPE,
    models:
      | Partial<NodeUserModel>
      | Partial<EdgeUserModel>
      | Partial<
          | ComboUserModel
          | Partial<NodeUserModel>[]
          | Partial<EdgeUserModel>[]
          | Partial<ComboUserModel>[]
        >,
    stack?: boolean,
  ):
    | NodeModel
    | EdgeModel
    | ComboModel
    | NodeModel[]
    | EdgeModel[]
    | ComboModel[] {
    
  }

  // 更新节点位置
  public updateNodePosition(
    models:
      | Partial<NodeUserModel>
      | Partial<
          ComboUserModel | Partial<NodeUserModel>[] | Partial<ComboUserModel>[]
        >,
    stack?: boolean,
  ) {
    
  }
  
  // 显示类型元素
  public showItem(ids: ID | ID[], disableAniamte?: boolean) {
    
  }
  
  // 隐藏类型元素
  public hideItem(ids: ID | ID[], disableAniamte?: boolean) {
    
  }
  
  // 设置类型元素状态
  public setItemState(
    ids: ID | ID[],
    states: string | string[],
    value: boolean,
  ) {
    
  }
  
  // 获取类型元素状态
  public getItemState(id: ID, state: string) {
    
  }

  // 清空类型元素状态
  public clearItemState(ids: ID | ID[], states?: string[]) {
    
  }

  // 获取渲染器box
  public getRenderBBox(id: ID | undefined): AABB | false {
    
  }

  // 获取显示类型id
  public getItemVisible(id: ID) {
    
  }

  // ===== combo operations =====
  
  // 创建组
  public createCombo(
    combo: string | ComboUserModel,
    childrenIds: string[],
    stack?: boolean,
  ) {
    
  }
 
  // 取消组
  public uncombo(comboId: ID, stack?: boolean) {
    
  }
  
  // 释放组
  public collapseCombo(comboId: ID, stack?: boolean) {
    
  }
  
  // 扩展组
  public expandCombo(comboId: ID, stack?: boolean) {
    
  }

  // ===== layout =====
  
  // 设置布局参数
  public async layout(options?: LayoutOptions) {
   
  }

  // 取消布局算法
  public stopLayout() {
    
  }

  // 设置交互模式
  public setMode(mode: string) {
    
  }

  // 添加交互行为
  public addBehaviors(
    behaviors: BehaviorOptionsOf<B>[],
    modes: string | string[],
  ) {
    
  }
  
  // 移除交互行为
  public removeBehaviors(behaviorKeys: string[], modes: string | string[]) {
    
  }

  // 更新交互行为
  public updateBehavior(behavior: BehaviorOptionsOf<B>, mode?: string) {
    
  }

  // 添加插件
  public addPlugins(
    pluginCfgs: (
      | {
          key: string;
          type: string;
          [cfgName: string]: unknown; // TODO: configs from plugins
        }
      | string
    )[],
  ) {
    
  }

  // 移除插件
  public removePlugins(pluginKeys: string[]) {
    
  }

  // 更新插件
  public updatePlugin(plugin: {
    key: string;
    type: string;
    [cfg: string]: unknown;
  }) {
    
  }

  // 绘制过渡动效
  public drawTransient(
    type: ITEM_TYPE | SHAPE_TYPE,
    id: ID,
    config: {
      action: 'remove' | 'add' | 'update' | undefined;
      style: ShapeStyle;
      onlyDrawKeyShape?: boolean;
    },
  ): DisplayObject {
    
  }

  // 销毁画布
  public destroy(callback?: Function) {
    
  }
}

StdLib

标准库用于提供和社区开发者进行交互的标准构件,方便自定义开发及共建共享。其中,提供了dataextensionsinteractionitemlayoutplugintheme以及viewport的插件化能力。

这里,以plugin的控制器接入为例,代码如下:

// https://github.com/antvis/G6/blob/v5/packages/g6/src/runtime/controller/plugin.ts

export class PluginController {
  public extensions: any = [];
  public graph: IGraph;

  /**
   * Plugins on graph.
   * @example
   * { 'minimap': Minimap, 'tooltip': Tooltip }
   */
  private pluginMap: Map<string, { type: string; plugin: Plugin }> = new Map();

  /**
   * Listeners added by all current plugins.
   * @example
   * {
   *   'minimap': { 'afterlayout': function },
   * }
   */
  private listenersMap: Record<string, Record<string, Listener>> = {};

  constructor(graph: IGraph<any, any>) {
    this.graph = graph;
    this.tap();
  }

  /**
   * Subscribe the lifecycle of graph.
   */
  private tap() {
    this.graph.hooks.init.tap(this.onPluginInit.bind(this));
    this.graph.hooks.pluginchange.tap(this.onPluginChange.bind(this));
    this.graph.hooks.destroy.tap(this.onDestroy.bind(this));
  }

  private onPluginInit() {
    // 1. Initialize new behaviors.
    this.pluginMap.clear();
    const { graph } = this;
    const pluginConfigs = graph.getSpecification().plugins || [];
    pluginConfigs.forEach((config) => {
      this.initPlugin(config);
    });

    // 2. Add listeners for each behavior.
    this.listenersMap = {};
    this.pluginMap.forEach((item, key) => {
      const { plugin } = item;
      this.addListeners(key, plugin);
    });
  }

  private initPlugin(config) {
    const { graph } = this;
    const Plugin = getExtension(config, registry.useLib, 'plugin');
    const options = typeof config === 'string' ? {} : config;
    const type = typeof config === 'string' ? config : config.type;
    const key = typeof config === 'string' ? config : config.key || type;
    const plugin = new Plugin(options);
    plugin.init(graph);
    this.pluginMap.set(key, { type, plugin });
    return { key, type, plugin };
  }

  private onPluginChange(params: {
    action: 'update' | 'add' | 'remove';
    plugins: (string | { key: string; type: string; options: any })[];
  }) {
    const { action, plugins: pluginCfgs } = params;
    if (action === 'add') {
      pluginCfgs.forEach((config) => {
        const { key, plugin } = this.initPlugin(config);
        this.addListeners(key, plugin);
      });
      return;
    }

    if (action === 'remove') {
      pluginCfgs.forEach((config) => {
        const key =
          typeof config === 'string' ? config : config.key || config.type;
        const item = this.pluginMap.get(key);
        if (!item) return;
        const { plugin } = item;
        this.removeListeners(key);
        plugin.destroy();
        this.pluginMap.delete(key);
      });
      return;
    }

    if (action === 'update') {
      pluginCfgs.forEach((config) => {
        if (typeof config === 'string') return;
        const key = config.key || config.type;
        const item = this.pluginMap.get(key);
        if (!item) return;
        const { plugin } = item;
        plugin.updateCfgs(config);
        this.removeListeners(key);
        this.addListeners(key, plugin);
      });
      return;
    }
  }

  private addListeners = (key: string, plugin: Plugin) => {
    const events = plugin.getEvents();
    this.listenersMap[key] = {};
    Object.keys(events).forEach((eventName) => {
      // Wrap the listener with error logging.
      const listener = wrapListener(
        key,
        eventName,
        events[eventName].bind(plugin),
      );
      this.graph.on(eventName, listener);
      this.listenersMap[key][eventName] = listener;
    });
  };

  private removeListeners = (key: string) => {
    const listeners = this.listenersMap[key];
    Object.keys(listeners).forEach((eventName) => {
      const listener = listeners[eventName];
      if (listener) {
        this.graph.off(eventName, listener);
      }
    });
  };

  private onDestroy() {
    this.pluginMap.forEach((item) => {
      const { plugin } = item;
      plugin.destroy();
    });
  }

  destroy() {}
}

而其他的标准库控制器,则通过配置的方式,在Graph初始化时进行接入,代码如下:

// https://github.com/antvis/G6/blob/v5/packages/g6/src/stdlib/index.ts
const stdLib = {
  transforms: {
    comboFromNode,
  },
  themes: {
    light: LightTheme,
    dark: DarkTheme,
  },
  themeSolvers: {
    spec: SpecThemeSolver,
    subject: SubjectThemeSolver,
  },
  layouts: layoutRegistry,
  behaviors: {
    'activate-relations': ActivateRelations,
    'drag-canvas': DragCanvas,
    'hover-activate': HoverActivate,
    'zoom-canvas': ZoomCanvas,
    'drag-node': DragNode,
    'click-select': ClickSelect,
    'brush-select': BrushSelect,
    'lasso-select': LassoSelect,
    'zoom-canvas-3d': ZoomCanvas3D,
    'rotate-canvas-3d': RotateCanvas3D,
    'track-canvas-3d': TrackCanvas3D,
    'orbit-canvas-3d': OrbitCanvas3D,
  },
  plugins: {
    minimap: Minimap,
    legend: Legend,
  },
  nodes: {
    'circle-node': CircleNode,
    'sphere-node': SphereNode,
  },
  edges: {
    'line-edge': LineEdge,
  },
  combos: {},
};

export { stdLib }

总结

综上所述,AntV G6 5.0提供了更好的插件化机制,将功能进行解耦后提供暴露的通用标准制式方便开发者能够更好的开源与共建共享,从而利用开源社区的力量将G6建设成为更加通用且适配更多场景的图可视化库。开源的商业逻辑从来不在开源本身,用闭源的思路做开源就失去了开源的价值,共勉!!!

参考

  • G6官网
  • G6源码
  • AntV 图发布会圆满收官,G6 5.0 招募社区大牛,共同拥抱开源!

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

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

相关文章

【玩转Linux操作】详细讲解expr,read,echo,printf,test,[]等命令

&#x1f38a;专栏【玩转Linux操作】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【free loop】 大一同学小吉&#xff0c;欢迎并且感谢大家指出我的问题&#x1f970; 文章目录 &#x1f354;expr命令⭐表达式说明 &#x1f3…

JAVA:Springboot动态装配Druid多数据源

1、简介 最近打算搭建一个鉴权中心服务&#xff0c;采用springbootFastMybatis装配Druid&#xff0c;考虑后续拓展采用Druid多数据源配置&#xff0c;以一个数据源为主&#xff0c;多个动态数据源为辅的结构。除了数据库&#xff0c;后续会结合shiro安全框架来搭建。 2、引用…

【Leetcode60天带刷】day33回溯算法——1005.K次取反后最大化的数组和 134. 加油站 135. 分发糖果

​ 题目&#xff1a; 1005. K 次取反后最大化的数组和 给你一个整数数组 nums 和一个整数 k &#xff0c;按以下方法修改该数组&#xff1a; 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。 重复这个过程恰好 k 次。可以多次选择同一个下标 i 。 以这种方式修改数组后&am…

将视频转为幻灯片图像:利用OpenCV实现视频资料转换的指南

视频成为了传播知识和信息的重要媒介之一。然而&#xff0c;有时我们需要以静态的形式保存视频内容&#xff0c;例如将视频讲座转换为幻灯片或图像&#xff0c;以便于分享、存档或打印。幸运的是&#xff0c;OpenCV这一功能强大的计算机视觉库提供了各种技术和工具&#xff0c;…

机器学习之线性回归算法

目录 线性回归算法 求导法推导 梯度下降法推导 线性回归实现人脸识别 导入数据 构建标签矩阵 经典线性回归求导法实现 经典线性回归梯度下降法实现 岭回归实现 套索回归实现 局部加权线性回归实现 可视化 人脸识别 线性回归算法 求导法推导 梯度下降法推导 线性回…

chatgpt赋能python:Title:Python编程中的空格怎么用?详细教程!

Title: Python编程中的空格怎么用&#xff1f;详细教程&#xff01; Introduction: Python编程的空格使用一直是令人困惑的话题之一&#xff0c;但它却是Python语言中非常重要的一部分。空格在Python程序中用来表示代码块的开始和结束&#xff0c;因此不同的空格使用方式可能…

【夜深人静学数据结构与算法 | 第十篇】动态规划

目录 前言&#xff1a; 动态规划&#xff1a; 常见应用&#xff1a; 解题步骤&#xff1a; 动态规划的简化步骤&#xff1a; 案例&#xff1a; 509. 斐波那契数 - 力扣&#xff08;LeetCode&#xff09; 70. 爬楼梯 - 力扣&#xff08;LeetCode&#xff09; 62. 不同路…

【软考网络管理员】2023年软考网管初级常见知识考点(10)- 网际协议IP及IPV6,IPV4详解

涉及知识点 分类的IP地址&#xff0c;子网划分&#xff0c;CIDR和路由汇聚&#xff0c;IPV4数据报格式&#xff0c;IPV6协议&#xff0c;软考网络管理员常考知识点&#xff0c;软考网络管理员网络安全&#xff0c;网络管理员考点汇总。 原创于&#xff1a;CSDN博主-《拄杖盲学…

Java的理论知识部分

文章目录 前言 一、Java的发展 1.1、Java的出现 1.2、Java官方网址 1.3、Java的平台 1.4、Java各版本新加的内容 1.5、java特点 1.6、Java的三种运行机制 1.7、Java的编译与运行 1.8、补充内容——华为鲲鹏jdk以及鲲鹏计算 二、面向对象程序编程 2.1、对象与类 2.2、Ja…

第一次安装cocoapods经历

先是执行&#xff1a;sudo gem install cocoapods 报错&#xff1a; ERROR: Error installing cocoapods: The last version of activesupport (> 5.0, < 8) to support your Ruby & RubyGems was 6.1.7.3. Try installing it with gem install activesupport -v…

无需麻烦,快速下载MySQL JDBC驱动程序!

如何提升你的MySQL数据库操作速度呢&#xff1f; 不必再费时寻找&#xff0c;我讲为你带来最简便、快速的MySQL JDBC驱动程序下载方法&#xff01; 无需繁琐步骤&#xff0c;轻松获取所需&#xff0c;让你的数据库操作更加流畅&#xff0c;事半功倍&#xff01;立即点击下载即…

高速数据采集专家--青翼8通道125MSPS 16位AD采集FMC子卡

青翼自研FMC129是一款8通道125MHz采样率16位AD采集FMC子卡&#xff0c;符合VITA57.1规范&#xff0c;可以作为一个理想的IO模块耦合至FPGA前端&#xff0c;8通道AD通过高带宽的FMC连接器&#xff08;HPC&#xff09;连接至FPGA从而大大降低了系统信号延迟。 该板卡支持板上可编…

【资料分享】Xilinx Zynq-7010/7020工业评估板规格书(双核ARM Cortex-A9 + FPGA,主频766MHz)

1 评估板简介 创龙科技TLZ7x-EasyEVM是一款基于Xilinx Zynq-7000系列XC7Z010/XC7Z020高性能低功耗处理器设计的异构多核SoC评估板&#xff0c;处理器集成PS端双核ARM Cortex-A9 PL端Artix-7架构28nm可编程逻辑资源&#xff0c;评估板由核心板和评估底板组成。核心板经过专业的…

常见排序及其改进方案

常见排序及其改进方案 快速排序 思想&#xff1a; 找到一个基准&#xff0c;通常来说选取左边第一个元素 定义中间变量temp接收基准值 两个哨兵i,j分别从数组左端、右端进行扫描 (a)先从右端开始扫描&#xff1a;哨兵j先从右端开始扫描&#xff0c;确保右端元素>基准值…

Collapse折叠面板(antd-design组件库)展示所有配置选项和onChange的作用

1.Collapse折叠面板 可以折叠/展开的内容区域。 2.何时使用 对复杂区域进行分组和隐藏&#xff0c;保持页面的整洁。 手风琴 是一种特殊的折叠面板&#xff0c;只允许单个内容区域展开。 组件代码来自&#xff1a; 分页 Pagination - Ant Design 3.本地验证前的准备 参考文章【…

Jmeter(二) - 从入门到精通 - 创建测试计划(Test Plan)(详解教程)

1.简介 上一篇文章已经教你把JMeter的测试环境搭建起来了&#xff0c;那么这一篇我们就将JMeter启动起来&#xff0c;一睹其芳容&#xff0c;首先我给大家介绍一下如何来创建一个测试计划&#xff08;Test Plan&#xff09;。 2.创建一个测试计划&#xff08;Test Plan&#x…

前端实现pdf,图片,word文件预览

前端实现文件预览功能 需求&#xff1a;实现一个在线预览pdf、excel、word、图片等文件的功能。 介绍&#xff1a;支持pdf、xlsx、docx、jpg、png、jpeg。 以下使用Vue3代码实现所有功能&#xff0c;建议以下的预览文件标签可以在外层包裹一层弹窗。 图片预览 iframe标签能够将…

Learning to cluster in order to transfer across domains and tasks (ICLR 2018)

Learning to cluster in order to transfer across domains and tasks (ICLR 2018) 摘要 这篇论文提出一个进行跨域/任务的迁移学除了习任务&#xff0c;并将其作为一个学习聚类的问题。除了特征&#xff0c;我们还可以迁移相似度信息&#xff0c;并且这是足以学习一个相似度…

Git的常用命令,及还原文件的指定版本,及分支管理

一.git 常用命令 1.创建一个空的Git仓库或重新初始化一个现有仓库 git init 2.执行 clone 命令默认会拉取远程仓库的所有内容 git clone 3.显示版本库和暂存区的状态 git status 4.将该文件添加到暂存区 git add . 5.将git add 之后文件在暂存区之后的提交 git commit -m 提…

【Java高级语法】(十七)Stream流式编程:释放流式编程的效率与优雅,狂肝万字只为全面讲透Stream流!~

Java高级语法详解之Stream流 1️⃣ 概念及特征2️⃣ 优势和缺点3️⃣ 使用3.1 语法3.2 常用API详解3.3 案例 4️⃣ 应用场景5️⃣ 使用技巧6️⃣ 并行流 ParallelStream&#x1f33e; 总结 1️⃣ 概念及特征 Java的Stream流是在Java 8中引入的一种用于处理集合数据的功能强大且…