SolidJS-每日小知识(9/14)

知识介绍

  1. 使用两个SVG进行主视图和Len视图的区分,len视图中附带额外信息

代码分析

1 使用两个SVG进行主视图和Len视图的区分

  1. 设置主视图
    // 绘制主SVG的函数
    const drawMainSVG = () => {
        // 选择主SVG并设置其属性
        const svgMain = d3.select(svgMainRef)
            .attr("width", 1000) // 设置宽度
            .attr("height", 700) // 设置高度
            .style('border', '1px solid black'); // 设置边框样式

        // 定义边距
        const margin = { top: 20, right: 20, bottom: 30, left: 60 };
        // 计算绘图区域的宽度和高度
        const width = +svgMain.attr('width') - margin.left - margin.right;
        const height = +svgMain.attr('height') - margin.top - margin.bottom;

        // 设置X和Y轴的最小最大值
        const axisXOffset = 10; // X轴偏移
        const axisYOffset = 50; // Y轴偏移
        const xMin = Math.min(
            (d3.min(xpoints(), d => d.coords[0])) - axisXOffset,
            (d3.min(ypoints(), d => d.coords[0])) - axisXOffset
        );
        const xMax = Math.max(
            (d3.max(xpoints(), d => d.coords[0])) + axisXOffset,
            (d3.min(ypoints(), d => d.coords[0])) + axisXOffset
        );
        const yMin = Math.min(
            (d3.min(xpoints(), d => d.coords[1])) - axisYOffset,
            (d3.min(ypoints(), d => d.coords[1])) - axisYOffset
        );
        const yMax = Math.max(
            (d3.max(xpoints(), d => d.coords[1])) + axisYOffset,
            (d3.min(ypoints(), d => d.coords[1])) + axisYOffset
        );

        // 创建X轴和Y轴的比例尺
        const x = d3.scaleLinear()
            .domain([xMin, xMax]) // 输入数据范围
            .range([0, width]); // 输出像素范围

        const y = d3.scaleLinear()
            .domain([yMin, yMax]) // 输入数据范围
            .range([height, 0]); // 输出像素范围

        // 创建一个g元素用于绘制内容
        const g = svgMain.append("g")
            .attr("transform", `translate(${margin.left},${margin.top})`); // 设置内容位置

        // 绘制X坐标点的圆圈
        g.selectAll('circle.jsonData')
            .data(xpoints()) // 绑定数据
            .enter().append('circle') // 创建圆元素
            .attr('class', 'jsonData') // 设置类名
            .attr('cx', d => x(d.coords[0])) // 设置圆心X坐标
            .attr('cy', d => y(d.coords[1])) // 设置圆心Y坐标
            .attr('r', 2) // 设置圆的半径
            .attr('fill', 'steelblue'); // 设置圆的填充颜色

        // 绘制Y坐标点的圆圈
        g.selectAll('circle.drawingData')
            .data(ypoints()) // 绑定数据
            .enter().append('circle') // 创建圆元素
            .attr('class', 'drawingData') // 设置类名
            .attr('cx', d => x(d.coords[0])) // 设置圆心X坐标
            .attr('cy', d => y(d.coords[1])) // 设置圆心Y坐标
            .attr('r', 2) // 设置圆的半径
            .attr('fill', 'red') // 设置圆的填充颜色
            .on('mouseover', (event, d) => { // 鼠标悬停事件
                const tooltip = d3.select('body') // 选择body元素创建tooltip
                    .append('div')
                    .attr('class', 'tooltip')
                    .style('position', 'absolute') // 绝对定位
                    .style('background-color', 'white') // 背景色
                    .style('border', '1px solid black') // 边框样式
                    .style('padding', '5px') // 内边距
                    .style('pointer-events', 'none') // 不响应鼠标事件
                    .style('left', `${event.pageX + 10}px`) // 设置tooltip左位置
                    .style('top', `${event.pageY + 10}px`) // 设置tooltip顶部位置
                    .text(d.text); // 显示文本内容
            })
            .on('mouseout', () => {
                d3.select('.tooltip').remove(); // 鼠标移出时移除tooltip
            });

        // 绘制X坐标点的文本标签
        g.selectAll('text')
            .data(xpoints()) // 绑定数据
            .enter().append('text') // 创建文本元素
            .attr('x', d => x(d.coords[0]) - 8) // 设置文本X位置
            .attr('y', d => y(d.coords[1]) + 8) // 设置文本Y位置
            .text(d => d.name) // 设置文本内容
            .attr('font-family', 'sans-serif') // 设置字体
            .attr('font-size', '5px') // 设置字体大小
            .attr('fill', 'black'); // 设置文本颜色

        // 绘制连接线
        g.selectAll('line.connection')
            .data(connections()) // 绑定数据
            .enter().append('line') // 创建线元素
            .attr('class', 'connection') // 设置类名
            .attr('x1', d => x(d.X_point[0])) // 连接线起点X
            .attr('y1', d => y(d.X_point[1])) // 连接线起点Y
            .attr('x2', d => x(d.Y_transformed_point[0])) // 连接线终点X
            .attr('y2', d => y(d.Y_transformed_point[1])) // 连接线终点Y
            .attr('stroke-width', 1) // 线宽
            .attr('stroke', d => {
                // 根据条件设置连接线的颜色
                if (d.matched === 1) {
                    return 'green'; // 匹配的连接线为绿色
                } else {
                    return 'orange'; // 其他情况使用橙色
                }
            });

        // 绘制X轴和Y轴
        g.append('g')
            .attr('transform', `translate(0,${height})`) // 设置Y轴位置
            .call(d3.axisBottom(x)); // 绘制X轴

        g.append('g')
            .call(d3.axisLeft(y)); // 绘制Y轴

        // 添加缩放行为
        const zoomBehavior = zoom()
            .scaleExtent([0.5, 5]) // 设置缩放范围
            .on('zoom', (event) => {
                g.attr('transform', event.transform); // 应用缩放变换到g元素
                updateLenSVG(event.transform); // 更新Len SVG的视图
            });

        svgMain.call(zoomBehavior); // 应用缩放行为到主SVG上
    }
  1. 设置Len视图
    // 绘制辅助SVG的函数
    const drawLenSVG = () => {
        const svgLen = d3.select(svgLenRef)
            .attr("width", 500) // 设置宽度
            .attr("height", 400) // 设置高度
            .style('margin-left', '10px') // 设置左边距
            .style('border', '1px solid black'); // 设置边框样式

        // 创建一个g元素用于放置内容
        const g = svgLen.append('g');

        // 复制主SVG中的内容到辅助SVG
        g.html(d3.select(svgMainRef).select('g').html());

        // 初始化辅助SVG的视图
        updateLenSVG(d3.zoomIdentity);
    };

2.1 对Len视图添加g元素,并复制主SVG中的内容到Len SVG

        // 创建一个g元素用于放置内容
        const g = svgLen.append('g');

        // 复制主SVG中的内容到辅助SVG
        g.html(d3.select(svgMainRef).select('g').html());

2.2 初始化LenSVG的视图(函数下一步实现)
updateLenSVG(d3.zoomIdentity);
3. 实现updateLenSVG函数

    // 更新辅助SVG的缩放函数
    function updateLenSVG(transform) {
        const svgLen = d3.select(svgLenRef); // 选择辅助SVG
        const g = svgLen.select('g'); // 选择g元素

        // 获取主SVG的尺寸
        const width = +d3.select(svgMainRef).attr('width');
        const height = +d3.select(svgMainRef).attr('height');

        // 计算放大区域的边界
        const centerX = width / 2; // 计算中心X坐标
        const centerY = height / 2; // 计算中心Y坐标
        const rectX = (centerX - 250) / transform.k - transform.x / transform.k; // 计算边界X
        const rectY = (centerY - 200) / transform.k - transform.y / transform.k; // 计算边界Y

        // 设置辅助SVG的视图范围
        g.attr('transform', `translate(${-rectX * transform.k}, ${-rectY * transform.k}) scale(${transform.k})`);

        // 更新辅助SVG中的内容
        g.html(d3.select(svgMainRef).select('g').html());

        // 重新计算X和Y的最小最大值
        const axisXOffset = 10; // X轴偏移
        const axisYOffset = 50; // Y轴偏移
        const xMin = Math.min(
            (d3.min(xpoints(), d => d.coords[0])) - axisXOffset,
            (d3.min(ypoints(), d => d.coords[0])) - axisXOffset
        );
        const xMax = Math.max(
            (d3.max(xpoints(), d => d.coords[0])) + axisXOffset,
            (d3.min(ypoints(), d => d.coords[0])) + axisXOffset
        );
        const yMin = Math.min(
            (d3.min(xpoints(), d => d.coords[1])) - axisYOffset,
            (d3.min(ypoints(), d => d.coords[1])) - axisYOffset
        );
        const yMax = Math.max(
            (d3.max(xpoints(), d => d.coords[1])) + axisYOffset,
            (d3.min(ypoints(), d => d.coords[1])) + axisYOffset
        );

        // 创建新的比例尺
        const x = d3.scaleLinear()
            .domain([xMin, xMax]) // 输入范围
            .range([0, width - 80]); // 输出范围

        const y = d3.scaleLinear()
            .domain([yMin, yMax]) // 输入范围
            .range([height - 50, 0]); // 输出范围

        // 更新Y坐标点的圆圈位置
        g.selectAll('circle.drawingData')
            .data(ypoints()) // 绑定数据
            .attr('cx', d => x(d.coords[0])) // 更新X坐标
            .attr('cy', d => y(d.coords[1])); // 更新Y坐标

        // 移除旧的文本标签
        g.selectAll('text.drawingData').remove();
        // 重新绘制文本标签
        g.selectAll('circle.drawingData')
          .each(function(d) {
            const circle = d3.select(this);
            g.append('text')
              .attr('class','drawingData')
              .attr('x', circle.attr('cx'))
              .attr('y', parseFloat(circle.attr('cy')) - 10)
              .text(d.text)
              .attr('font-family', 'sans-serif')
              .attr('font-size', '10px')
              .attr('fill', 'red')
              .attr('text-anchor', 'middle');
        });
    }

3.1 设置Len视图的视图范围,更新内容

        const svgLen = d3.select(svgLenRef); // 选择辅助SVG
        const g = svgLen.select('g'); // 选择g元素

        // 获取主SVG的尺寸
        const width = +d3.select(svgMainRef).attr('width');
        const height = +d3.select(svgMainRef).attr('height');

        // 计算放大区域的边界
        const centerX = width / 2; // 计算中心X坐标
        const centerY = height / 2; // 计算中心Y坐标
        const rectX = (centerX - 250) / transform.k - transform.x / transform.k; // 计算边界X
        const rectY = (centerY - 200) / transform.k - transform.y / transform.k; // 计算边界Y

        // 设置辅助SVG的视图范围
        g.attr('transform', `translate(${-rectX * transform.k}, ${-rectY * transform.k}) scale(${transform.k})`);

        // 更新辅助SVG中的内容
        g.html(d3.select(svgMainRef).select('g').html());

3.2 因为想使redCircles附带新信息,所以重新绘制
请注意:重新绘制len视图的数据时,其相对位置与main视图中存在偏差,所以要手动测试偏移量viewOffetWidthviewOffetHeight

        // 重新计算X和Y的最小最大值
        const axisXOffset = 10; // X轴偏移
        const axisYOffset = 50; // Y轴偏移
        const xMin = Math.min(
            (d3.min(xpoints(), d => d.coords[0])) - axisXOffset,
            (d3.min(ypoints(), d => d.coords[0])) - axisXOffset
        );
        const xMax = Math.max(
            (d3.max(xpoints(), d => d.coords[0])) + axisXOffset,
            (d3.min(ypoints(), d => d.coords[0])) + axisXOffset
        );
        const yMin = Math.min(
            (d3.min(xpoints(), d => d.coords[1])) - axisYOffset,
            (d3.min(ypoints(), d => d.coords[1])) - axisYOffset
        );
        const yMax = Math.max(
            (d3.max(xpoints(), d => d.coords[1])) + axisYOffset,
            (d3.min(ypoints(), d => d.coords[1])) + axisYOffset
        );

        const viewOffetWidth= -80;
        const viewOffetHeight= -50;

        // 创建新的比例尺
        const x = d3.scaleLinear()
            .domain([xMin, xMax]) // 输入范围
            .range([0, width + viewOffetWidth]); // 输出范围

        const y = d3.scaleLinear()
            .domain([yMin, yMax]) // 输入范围
            .range([height + viewOffetHeight, 0]); // 输出范围

        // 更新Y坐标点的圆圈位置
        g.selectAll('circle.drawingData')
            .data(ypoints()) // 绑定数据
            .attr('cx', d => x(d.coords[0])) // 更新X坐标
            .attr('cy', d => y(d.coords[1])); // 更新Y坐标

        // 移除旧的文本标签
        g.selectAll('text.drawingData').remove();
        // 重新绘制文本标签
        g.selectAll('circle.drawingData')
          .each(function(d) {
            const circle = d3.select(this);
            g.append('text')
              .attr('class','drawingData')
              .attr('x', circle.attr('cx'))
              .attr('y', parseFloat(circle.attr('cy')) - 10)
              .text(d.text)
              .attr('font-family', 'sans-serif')
              .attr('font-size', '10px')
              .attr('fill', 'red')
              .attr('text-anchor', 'middle');
        });

完整代码

  1. 使用两个SVG进行主视图和Len视图的区分,len视图中附带额外信息
import { createSignal, onMount } from 'solid-js'; // 导入Solid.js库中的createSignal和onMount
import * as d3 from 'd3'; // 导入D3.js库
import { zoom } from 'd3-zoom'; // 从D3.js导入缩放功能
import { Coordinates, XPoint, YTransformedPoint, Connection, MatchData } from "../utils/type"; // 导入类型定义

const Temp = () => {
    // 创建状态信号用于存储不同类型的数据
    const [data, setData] = createSignal<MatchData | null>(null); // 存储匹配数据
    const [xpoints, setXpoints] = createSignal<XPoint[]>([]); // 存储X坐标点
    const [ypoints, setYpoints] = createSignal<YTransformedPoint[]>([]); // 存储变换后的Y坐标点
    const [connections, setConnections] = createSignal<Connection[]>([]); // 存储连接信息

    let svgMainRef: SVGSVGElement | undefined; // 主SVG图形的引用
    let svgLenRef: SVGSVGElement | undefined; // 辅助SVG图形的引用

    // 组件挂载时执行的数据加载
    onMount(async () => {
        try {
            // 从指定路径获取数据
            const response = await fetch('./src/assets/dataset/matched_points_info.json');
            if (!response.ok) {
                throw new Error(`HTTP 错误!状态码:${response.status}`); // 若响应不OK则抛出错误
            }
            const jsonData: MatchData = await response.json(); // 解析JSON数据
            setData(jsonData); // 设置状态信号中存储的数据
            setXpoints(jsonData.X_points); // 设置X坐标点
            setYpoints(jsonData.Y_transformed_points); // 设置Y坐标点
            setConnections(jsonData.connections); // 设置连接信息
            
            drawMainSVG(); // 调用绘制主SVG的函数
            drawLenSVG(); // 调用绘制辅助SVG的函数

        } catch (error) {
            console.error("加载数据时出错:", error); // 捕获并输出错误信息
        }
    });

    // 绘制主SVG的函数
    const drawMainSVG = () => {
        // 选择主SVG并设置其属性
        const svgMain = d3.select(svgMainRef)
            .attr("width", 1000) // 设置宽度
            .attr("height", 700) // 设置高度
            .style('border', '1px solid black'); // 设置边框样式

        // 定义边距
        const margin = { top: 20, right: 20, bottom: 30, left: 60 };
        // 计算绘图区域的宽度和高度
        const width = +svgMain.attr('width') - margin.left - margin.right;
        const height = +svgMain.attr('height') - margin.top - margin.bottom;

        // 设置X和Y轴的最小最大值
        const axisXOffset = 10; // X轴偏移
        const axisYOffset = 50; // Y轴偏移
        const xMin = Math.min(
            (d3.min(xpoints(), d => d.coords[0])) - axisXOffset,
            (d3.min(ypoints(), d => d.coords[0])) - axisXOffset
        );
        const xMax = Math.max(
            (d3.max(xpoints(), d => d.coords[0])) + axisXOffset,
            (d3.min(ypoints(), d => d.coords[0])) + axisXOffset
        );
        const yMin = Math.min(
            (d3.min(xpoints(), d => d.coords[1])) - axisYOffset,
            (d3.min(ypoints(), d => d.coords[1])) - axisYOffset
        );
        const yMax = Math.max(
            (d3.max(xpoints(), d => d.coords[1])) + axisYOffset,
            (d3.min(ypoints(), d => d.coords[1])) + axisYOffset
        );

        // 创建X轴和Y轴的比例尺
        const x = d3.scaleLinear()
            .domain([xMin, xMax]) // 输入数据范围
            .range([0, width]); // 输出像素范围

        const y = d3.scaleLinear()
            .domain([yMin, yMax]) // 输入数据范围
            .range([height, 0]); // 输出像素范围

        // 创建一个g元素用于绘制内容
        const g = svgMain.append("g")
            .attr("transform", `translate(${margin.left},${margin.top})`); // 设置内容位置

        // 绘制X坐标点的圆圈
        g.selectAll('circle.jsonData')
            .data(xpoints()) // 绑定数据
            .enter().append('circle') // 创建圆元素
            .attr('class', 'jsonData') // 设置类名
            .attr('cx', d => x(d.coords[0])) // 设置圆心X坐标
            .attr('cy', d => y(d.coords[1])) // 设置圆心Y坐标
            .attr('r', 2) // 设置圆的半径
            .attr('fill', 'steelblue'); // 设置圆的填充颜色

        // 绘制Y坐标点的圆圈
        g.selectAll('circle.drawingData')
            .data(ypoints()) // 绑定数据
            .enter().append('circle') // 创建圆元素
            .attr('class', 'drawingData') // 设置类名
            .attr('cx', d => x(d.coords[0])) // 设置圆心X坐标
            .attr('cy', d => y(d.coords[1])) // 设置圆心Y坐标
            .attr('r', 2) // 设置圆的半径
            .attr('fill', 'red') // 设置圆的填充颜色
            .on('mouseover', (event, d) => { // 鼠标悬停事件
                const tooltip = d3.select('body') // 选择body元素创建tooltip
                    .append('div')
                    .attr('class', 'tooltip')
                    .style('position', 'absolute') // 绝对定位
                    .style('background-color', 'white') // 背景色
                    .style('border', '1px solid black') // 边框样式
                    .style('padding', '5px') // 内边距
                    .style('pointer-events', 'none') // 不响应鼠标事件
                    .style('left', `${event.pageX + 10}px`) // 设置tooltip左位置
                    .style('top', `${event.pageY + 10}px`) // 设置tooltip顶部位置
                    .text(d.text); // 显示文本内容
            })
            .on('mouseout', () => {
                d3.select('.tooltip').remove(); // 鼠标移出时移除tooltip
            });

        // 绘制X坐标点的文本标签
        g.selectAll('text')
            .data(xpoints()) // 绑定数据
            .enter().append('text') // 创建文本元素
            .attr('x', d => x(d.coords[0]) - 8) // 设置文本X位置
            .attr('y', d => y(d.coords[1]) + 8) // 设置文本Y位置
            .text(d => d.name) // 设置文本内容
            .attr('font-family', 'sans-serif') // 设置字体
            .attr('font-size', '5px') // 设置字体大小
            .attr('fill', 'black'); // 设置文本颜色

        // 绘制连接线
        g.selectAll('line.connection')
            .data(connections()) // 绑定数据
            .enter().append('line') // 创建线元素
            .attr('class', 'connection') // 设置类名
            .attr('x1', d => x(d.X_point[0])) // 连接线起点X
            .attr('y1', d => y(d.X_point[1])) // 连接线起点Y
            .attr('x2', d => x(d.Y_transformed_point[0])) // 连接线终点X
            .attr('y2', d => y(d.Y_transformed_point[1])) // 连接线终点Y
            .attr('stroke-width', 1) // 线宽
            .attr('stroke', d => {
                // 根据条件设置连接线的颜色
                if (d.matched === 1) {
                    return 'green'; // 匹配的连接线为绿色
                } else {
                    return 'orange'; // 其他情况使用橙色
                }
            });

        // 绘制X轴和Y轴
        g.append('g')
            .attr('transform', `translate(0,${height})`) // 设置Y轴位置
            .call(d3.axisBottom(x)); // 绘制X轴

        g.append('g')
            .call(d3.axisLeft(y)); // 绘制Y轴

        // 添加缩放行为
        const zoomBehavior = zoom()
            .scaleExtent([0.5, 5]) // 设置缩放范围
            .on('zoom', (event) => {
                g.attr('transform', event.transform); // 应用缩放变换到g元素
                updateLenSVG(event.transform); // 更新Len SVG的视图
            });

        svgMain.call(zoomBehavior); // 应用缩放行为到主SVG上
    }

    // 绘制辅助SVG的函数
    const drawLenSVG = () => {
        const svgLen = d3.select(svgLenRef)
            .attr("width", 500) // 设置宽度
            .attr("height", 400) // 设置高度
            .style('margin-left', '10px') // 设置左边距
            .style('border', '1px solid black'); // 设置边框样式

        // 创建一个g元素用于放置内容
        const g = svgLen.append('g');

        // 复制主SVG中的内容到辅助SVG
        g.html(d3.select(svgMainRef).select('g').html());

        // 初始化辅助SVG的视图
        updateLenSVG(d3.zoomIdentity);
    };

    // 更新辅助SVG的缩放函数
    function updateLenSVG(transform) {
        const svgLen = d3.select(svgLenRef); // 选择辅助SVG
        const g = svgLen.select('g'); // 选择g元素

        // 获取主SVG的尺寸
        const width = +d3.select(svgMainRef).attr('width');
        const height = +d3.select(svgMainRef).attr('height');

        // 计算放大区域的边界
        const centerX = width / 2; // 计算中心X坐标
        const centerY = height / 2; // 计算中心Y坐标
        const rectX = (centerX - 250) / transform.k - transform.x / transform.k; // 计算边界X
        const rectY = (centerY - 200) / transform.k - transform.y / transform.k; // 计算边界Y

        // 设置辅助SVG的视图范围
        g.attr('transform', `translate(${-rectX * transform.k}, ${-rectY * transform.k}) scale(${transform.k})`);

        // 更新辅助SVG中的内容
        g.html(d3.select(svgMainRef).select('g').html());

        // 重新计算X和Y的最小最大值
        const axisXOffset = 10; // X轴偏移
        const axisYOffset = 50; // Y轴偏移
        const xMin = Math.min(
            (d3.min(xpoints(), d => d.coords[0])) - axisXOffset,
            (d3.min(ypoints(), d => d.coords[0])) - axisXOffset
        );
        const xMax = Math.max(
            (d3.max(xpoints(), d => d.coords[0])) + axisXOffset,
            (d3.min(ypoints(), d => d.coords[0])) + axisXOffset
        );
        const yMin = Math.min(
            (d3.min(xpoints(), d => d.coords[1])) - axisYOffset,
            (d3.min(ypoints(), d => d.coords[1])) - axisYOffset
        );
        const yMax = Math.max(
            (d3.max(xpoints(), d => d.coords[1])) + axisYOffset,
            (d3.min(ypoints(), d => d.coords[1])) + axisYOffset
        );

        // 创建新的比例尺
        const x = d3.scaleLinear()
            .domain([xMin, xMax]) // 输入范围
            .range([0, width - 80]); // 输出范围

        const y = d3.scaleLinear()
            .domain([yMin, yMax]) // 输入范围
            .range([height - 50, 0]); // 输出范围

        // 更新Y坐标点的圆圈位置
        g.selectAll('circle.drawingData')
            .data(ypoints()) // 绑定数据
            .attr('cx', d => x(d.coords[0])) // 更新X坐标
            .attr('cy', d => y(d.coords[1])); // 更新Y坐标

        // 移除旧的文本标签
        g.selectAll('text.drawingData').remove();
        // 重新绘制文本标签
        g.selectAll('circle.drawingData')
          .each(function(d) {
            const circle = d3.select(this);
            g.append('text')
              .attr('class','drawingData')
              .attr('x', circle.attr('cx'))
              .attr('y', parseFloat(circle.attr('cy')) - 10)
              .text(d.text)
              .attr('font-family', 'sans-serif')
              .attr('font-size', '10px')
              .attr('fill', 'red')
              .attr('text-anchor', 'middle');
        });
    }

    // 返回包含两个SVG图形的JSX元素
    return (
        <div style={{ display: 'flex' }}>
            <svg ref={svgMainRef}></svg> {/* 主SVG */}
            <svg ref={svgLenRef}></svg> {/* 辅助SVG */}
        </div>
    );
}

export default Temp; // 导出Temp组件

posted @   梧桐灯下江楚滢  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示