当前位置: 首页 > news >正文

扫地机如何高效的实现轨迹

1、如果有几万个轨迹点, 实时绘制,如何才能保证不卡顿呢?

(考虑分段绘制, 光栅化等)

@interface SXLaserPathLayer : UIView

@property (strong, nonatomic) NSArray <NSString *>*pathHidingType;

@property (strong, nonatomic) LCLaserPathModel *pathModel;

//@property (copy, nonatomic, nullable) NSDictionary *pathData;

@property (strong, nonatomic, readonly) NSArray<LCLaserPathPoint *> *pathPoints;

//地图配置

@property (nonatomic, weak, nullable) LCLaserMapConfig *mapConfig;

@property (nonatomic, assign) BOOL isCleaning; //是否正在清扫

- (void)clearLayer;

/**

 * 路径是否可以绘制

 */

- (BOOL)canRender;

/**

 * 触发路径绘制

 */

//- (void)renderWithOrigin:(CGPoint)origin;

//- (void)renderWithOrigin:(CGPoint)origin pointScaleUsingFunction:(LCPointConverter NS_NOESCAPE)converter;

- (void)renderWithOrigin:(CGPoint)origin

                  radius:(CGFloat)radius

                  offset:(CGPoint)offset;

- (void)renderWithOriginNew:(CGPoint)origin radius:(CGFloat)radius offset:(CGPoint)offset;

- (void)setZoomScale:(CGFloat)scale; //设置缩放因子

@end

#define pathLayerWidth 1.2

#define pathLayerFWidth 10

#define pathLayerNum 2000

@interface SXLaserPathSubLayer : CAShapeLayer

@property (assign, nonatomic) BOOL didAddSubLayer;

@property (copy, nonatomic) NSString *dataColor;

@property (strong, nonatomic) UIBezierPath *bezierPath;

@end

typedef NS_ENUM(NSUInteger, LCLaserPathStyle) {

    LCLaserPathNone,

    LCLaserPathSkip,

    LCLaserPathType,

};

@implementation SXLaserPathSubLayer

- (void)setDataColor:(NSString *)dataColor {

    _dataColor = dataColor;

    self.strokeColor = [LCColorWithHex(dataColor) CGColor];

    self.lineCap = kCALineCapRound;

    self.lineJoin = kCALineJoinRound;

    self.fillColor = nil;

}

- (void)setBezierPath:(UIBezierPath *)bezierPath {

    _bezierPath = bezierPath;

    bezierPath.lineCapStyle = kCGLineCapRound;

    bezierPath.lineJoinStyle = kCGLineJoinRound;

}

@end

@interface LCLaserPathLayer ()

@property (strong, nonatomic) NSMutableDictionary<NSString *, LCLaserPathSubLayer *> *layerMap;

@property (strong, nonatomic) LCLaserPathSubLayer *pathSubLayer;

@property (strong, nonatomic) LCLaserPathSubLayer *pathFloorLayer;

@property (strong, nonatomic) NSMutableArray<LCLaserPathSubLayer *> *pathFloorLayers; // 改为数组存储多个floor layer

@property (assign, nonatomic) LCLaserPathStyle pathStyle;

@property (strong, nonatomic, readwrite) NSArray<LCLaserPathPoint *> *pathPoints;

@property (assign, nonatomic) BOOL isSmoothed;

@property (strong, nonatomic) dispatch_semaphore_t semaphore;

@property (strong, nonatomic) dispatch_queue_t renderQueue;

@property (assign, nonatomic) CGFloat scale;//当前视图缩放比例

@end

@implementation LCLaserPathLayer

- (instancetype)initWithFrame:(CGRect)frame

{

    self = [super initWithFrame:frame];

    if (self) {

        self.userInteractionEnabled = false;

        _layerMap = [NSMutableDictionary dictionary];

        _pathFloorLayers = [NSMutableArray array];

        dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, 0);

        _renderQueue = dispatch_queue_create("lc.sweeper.laser.path", attr);

        _scale = 1.0; /// 初始缩放比例

        _isCleaning = NO;

        self.semaphore = dispatch_semaphore_create(1);

    }

    return self;

}

#pragma mark - Public

- (void)clearLayer {

    [self cleanLayerMap];

    [self cleanPathSubLayer];

}

- (void)cleanLayerMap {

    for (NSString *type in self.layerMap) {

        LCLaserPathSubLayer *layer = self.layerMap[type];

        layer.path = nil;

        [layer removeFromSuperlayer];

    }

    [self.layerMap removeAllObjects];

}

- (void)cleanPathSubLayer {

    if(_pathSubLayer) {

        _pathSubLayer.path = nil;

        [_pathSubLayer removeFromSuperlayer];

        _pathSubLayer = nil;

    }

    if(_pathFloorLayer) {

        _pathFloorLayer.path = nil;

        [_pathFloorLayer removeFromSuperlayer];

        _pathFloorLayer = nil;

    }

    // 清理所有floor layers

    for (LCLaserPathSubLayer *layer in _pathFloorLayers) {

        layer.path = nil;

        [layer removeFromSuperlayer];

    }

    [_pathFloorLayers removeAllObjects];

}

- (BOOL)canRender {

    BOOL canDraw = false;

    do {

        if ([self pathWidth] <= 0) {

            break;

        }

        if (_pathModel == nil) {

            break;

        }

        if (_pathPoints == nil) {

            break;

        }

        canDraw = true;

    } while (0);

    return canDraw;

}

- (void)renderWithOrigin:(CGPoint)origin radius:(CGFloat)radius offset:(CGPoint)offset {

//    if (![self canRender]) {

//        return;

//    }

   

    dispatch_async(self.renderQueue, ^{

        dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);

        [self cleanLayerMap];

#pragma mark - 会崩溃

        NSDictionary *tmpPathColors = [[NSDictionary alloc] initWithDictionary:self.pathModel.typeColors copyItems:YES];

        if (!_pathSubLayer) {

            LCLaserPathSubLayer *layer = [[LCLaserPathSubLayer alloc] init];

            layer.dataColor = tmpPathColors.allValues.firstObject;

            layer.bezierPath = [UIBezierPath bezierPath];

            _pathSubLayer = layer;

        } else {

            [_pathSubLayer.bezierPath removeAllPoints];

        }

        

#pragma - 涂抹 -- buyi

        if (!_pathFloorLayer) {

            LCLaserPathSubLayer *layer = [[LCLaserPathSubLayer alloc] init];

            layer.dataColor = tmpPathColors.allValues.firstObject;

            layer.bezierPath = [UIBezierPath bezierPath];

            // 必须设置的参数

            layer.shouldRasterize = YES;

            layer.rasterizationScale = [UIScreen mainScreen].scale;

              

              // 关键:设置光栅化内容的边界和锚点

            layer.contentsScale = [UIScreen mainScreen].scale;

            layer.needsDisplayOnBoundsChange = YES;

              

              // 优化绘制性能

            layer.drawsAsynchronously = YES;

            layer.allowsEdgeAntialiasing = YES;

              

              // 防止系统自动清理缓存

              if ([layer respondsToSelector:@selector(setContentsFormat:)]) {

                  // iOS 10+ 使用优化的内容格式

                  if (@available(iOS 10.0, *)) {

                      layer.contentsFormat = kCAContentsFormatRGBA8Uint;

                  }

              }

            

            _pathFloorLayer = layer;

        } else {

            [_pathFloorLayer.bezierPath removeAllPoints];

        }

        

        LCLaserPathPoint *nextPoint = nil;

        LCLaserPathPoint *tempPoint = [LCLaserPathPoint new];

        LCLaserPathPoint *tempNextPoint = [LCLaserPathPoint new];

        NSUInteger idx = 0;

#ifdef DEBUG

        CFTimeInterval start = CFAbsoluteTimeGetCurrent();

#endif

#pragma mark - 会崩溃

        NSInteger mergeFlag = 0;

        NSArray<LCLaserPathPoint *> *points = [self.pathPoints copy];

        for (LCLaserPathPoint *point in points) {

         

            // 坐标转换 这里外面layer只进行了Scale缩放,所以这里也只进行缩放即可,(偏移量待返回数据时再计算?)

            tempPoint.x = (point.x - offset.x) * radius;

            tempPoint.y = (point.y - offset.y) * radius;

            if ((points.count > 1) && (idx < (points.count - 1))) {

                nextPoint = [points objectAtIndex:idx + 1];

                if (nextPoint == nil) {

                    dispatch_semaphore_signal(self.semaphore);

                    return;

                }

                

                NSString *type = [NSString stringWithFormat:@"%@", point.typeNew];

                NSString *typeNext = [NSString stringWithFormat:@"%@", nextPoint.typeNew];

                if ([type isEqualToString:@"0"]) {

                   /// 不绘制

                } else {

                    // 下一个路径点

                    // 坐标转换

                    tempNextPoint.x = (nextPoint.x - offset.x) * radius;

                    tempNextPoint.y = (nextPoint.y - offset.y) * radius;

                    

                    if (![type isEqualToString:@"2"]) {

                        [_pathSubLayer.bezierPath moveToPoint: CGPointMake(tempPoint.x, tempPoint.y)];

                        if (![typeNext isEqualToString:@"0"]) {

                            [_pathSubLayer.bezierPath addLineToPoint: CGPointMake(tempNextPoint.x, tempNextPoint.y)];

                        }

                    

                    }

                  

                    if (![type isEqualToString:@"3"] ) {

                        [_pathFloorLayer.bezierPath moveToPoint: CGPointMake(tempPoint.x, tempPoint.y)];

                        if (![typeNext isEqualToString:@"0"]) {

                            [_pathFloorLayer.bezierPath addLineToPoint: CGPointMake(tempNextPoint.x, tempNextPoint.y)];

                        }

                    }

                }

            }

            idx++;

        }

#pragma - 清扫轨迹的颜色 和宽度 -- buyi

        dispatch_async(dispatch_get_main_queue(), ^{

#pragma - 涂抹

            if (!_pathFloorLayer.didAddSubLayer) {

                [self.layer addSublayer:_pathFloorLayer];

                _pathFloorLayer.didAddSubLayer = YES;

            }

            if(points.count > 1500 ) {

//                _pathFloorLayer.shouldRasterize = YES;

                _pathFloorLayer.lineWidth = pathLayerFWidth ;

            } else {

//                _pathFloorLayer.shouldRasterize = NO;

                _pathFloorLayer.lineWidth = pathLayerFWidth;

            }

           

            // 创建具有 30% 不透明度的白色

            UIColor *whiteWithOpacity = [[UIColor whiteColor] colorWithAlphaComponent:0.5];

            _pathFloorLayer.strokeColor = whiteWithOpacity.CGColor;

            _pathFloorLayer.path = nil;

        

            _pathFloorLayer.path = [_pathFloorLayer.bezierPath CGPath];

           

            if (!_pathSubLayer.didAddSubLayer) {

                [self.layer addSublayer:_pathSubLayer];

                _pathSubLayer.didAddSubLayer = YES;

            }

            if(points.count > 1500 && points.count < 8000) {

                _pathSubLayer.lineWidth = [self pathWidth];//[self pathWidth];

                _pathSubLayer.shouldRasterize = NO;

            } else if(points.count >= 8000) {

                _pathSubLayer.lineWidth = [self pathWidth];//[self pathWidth];

                _pathSubLayer.shouldRasterize = YES;

            }else {

                _pathSubLayer.lineWidth = 1.2;

                _pathSubLayer.shouldRasterize = NO;

            }

            // 必须设置的参数

            _pathSubLayer.rasterizationScale = [UIScreen mainScreen].scale;

              

              // 关键:设置光栅化内容的边界和锚点

            _pathSubLayer.contentsScale = [UIScreen mainScreen].scale;

            _pathSubLayer.needsDisplayOnBoundsChange = YES;

              

              // 优化绘制性能

            _pathSubLayer.drawsAsynchronously = YES;

            _pathSubLayer.allowsEdgeAntialiasing = YES;

              

              // 防止系统自动清理缓存

              if ([_pathSubLayer respondsToSelector:@selector(setContentsFormat:)]) {

                  // iOS 10+ 使用优化的内容格式

                  if (@available(iOS 10.0, *)) {

                      _pathSubLayer.contentsFormat = kCAContentsFormatRGBA8Uint;

                  }

              }

//            _pathSubLayer.allowsEdgeAntialiasing = YES;

//            // 其他优化设置

//            _pathSubLayer.drawsAsynchronously = YES; // 异步绘制,提高性能

//            _pathSubLayer.rasterizationScale = [UIScreen mainScreen].scale;

        

            _pathSubLayer.strokeColor = [UIColor whiteColor].CGColor;

            _pathSubLayer.path = nil;

            _pathSubLayer.path = [_pathSubLayer.bezierPath CGPath];

            dispatch_semaphore_signal(self.semaphore);

            

#ifdef DEBUG

        NSLog(@"轨迹绘制结束  path: %f ms count: %d", (CFAbsoluteTimeGetCurrent() - start) * 1000, points.count);

#endif

        });

    });

}

- (void)renderWithOriginNew:(CGPoint)origin radius:(CGFloat)radius offset:(CGPoint)offset {

    

    dispatch_async(self.renderQueue, ^{

        dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);

        [self cleanLayerMap];

        

        NSDictionary *tmpPathColors = [[NSDictionary alloc] initWithDictionary:self.pathModel.typeColors copyItems:YES];

        if (!_pathSubLayer) {

            LCLaserPathSubLayer *layer = [[LCLaserPathSubLayer alloc] init];

            layer.dataColor = tmpPathColors.allValues.firstObject;

            layer.bezierPath = [UIBezierPath bezierPath];

            _pathSubLayer = layer;

            // 设置细轨迹图层在宽轨迹之上

            _pathSubLayer.zPosition = 1.0;

        } else {

            [_pathSubLayer.bezierPath removeAllPoints];

        }

        

        // 清理并重新创建floor layers

        for (LCLaserPathSubLayer *layer in _pathFloorLayers) {

            layer.path = nil;

            [layer removeFromSuperlayer];

        }

        [_pathFloorLayers removeAllObjects];

        

        LCLaserPathPoint *nextPoint = nil;

        LCLaserPathPoint *tempPoint = [LCLaserPathPoint new];

        LCLaserPathPoint *tempNextPoint = [LCLaserPathPoint new];

        NSUInteger idx = 0;

        

#ifdef DEBUG

        CFTimeInterval start = CFAbsoluteTimeGetCurrent();

#endif

        

        NSArray<LCLaserPathPoint *> *points = [self.pathPoints copy];

        

        // 分段绘制宽轨迹 - 按一定点数分段

        NSInteger segmentPointCount = pathLayerNum; // 每2000个点分为一段

        NSInteger segmentCount = ceil((CGFloat)points.count / segmentPointCount);

        

        // 创建分段layer

        for (NSInteger i = 0; i < segmentCount; i++) {

            LCLaserPathSubLayer *floorLayer = [[LCLaserPathSubLayer alloc] init];

            floorLayer.dataColor = tmpPathColors.allValues.firstObject;

            floorLayer.bezierPath = [UIBezierPath bezierPath];

            floorLayer.lineWidth = pathLayerFWidth;//[self calculateFloorLineWidthForPointsCount:points.count];

            floorLayer.shouldRasterize = YES;

            // 创建具有 30% 不透明度的白色

            UIColor *whiteWithOpacity = [[UIColor whiteColor] colorWithAlphaComponent:0.5];

            floorLayer.strokeColor = whiteWithOpacity.CGColor;

            

            // 设置宽轨迹图层在细轨迹之下

            floorLayer.zPosition = 0.0;

//            floorLayer.compositingFilter = nil; // 默认就是正常混合模式

//            // 2. 对于图层,可以设置 shouldRasterize 来优化渲染

//            floorLayer.allowsEdgeAntialiasing = YES;

//

//            // 其他优化设置

//            floorLayer.drawsAsynchronously = YES; // 异步绘制,提高性能

//            floorLayer.shouldRasterize = YES;

//            floorLayer.rasterizationScale = [UIScreen mainScreen].scale;

            

            // 必须设置的参数

            floorLayer.shouldRasterize = YES;

            floorLayer.rasterizationScale = [UIScreen mainScreen].scale;

              

              // 关键:设置光栅化内容的边界和锚点

            floorLayer.contentsScale = [UIScreen mainScreen].scale;

            floorLayer.needsDisplayOnBoundsChange = YES;

              

              // 优化绘制性能

            floorLayer.drawsAsynchronously = YES;

            floorLayer.allowsEdgeAntialiasing = YES;

              

              // 防止系统自动清理缓存

              if ([floorLayer respondsToSelector:@selector(setContentsFormat:)]) {

                  // iOS 10+ 使用优化的内容格式

                  if (@available(iOS 10.0, *)) {

                      floorLayer.contentsFormat = kCAContentsFormatRGBA8Uint;

                  }

              }

            

            [_pathFloorLayers addObject:floorLayer];

        }

        

        NSInteger currentSegment = 0;

        LCLaserPathSubLayer *currentFloorLayer = _pathFloorLayers.firstObject;

        

        for (LCLaserPathPoint *point in points) {

            // 检查是否需要切换到下一个segment

            if (idx > 0 && idx % segmentPointCount == 0 && currentSegment < segmentCount - 1) {

                currentSegment++;

                if (currentSegment < _pathFloorLayers.count) {

                    currentFloorLayer = _pathFloorLayers[currentSegment];

                }

           

            }

            

            // 坐标转换

            tempPoint.x = (point.x - offset.x) * radius;

            tempPoint.y = (point.y - offset.y) * radius;

            

            if ((points.count > 1) && (idx < (points.count - 1))) {

                nextPoint = [points objectAtIndex:idx + 1];

                if (nextPoint == nil) {

                    dispatch_semaphore_signal(self.semaphore);

                    return;

                }

                

                NSString *type = [NSString stringWithFormat:@"%@", point.typeNew];

                NSString *typeNext = [NSString stringWithFormat:@"%@", nextPoint.typeNew];

                if ([type isEqualToString:@"0"]) {

                    

                } else {

                    // 下一个路径点

                    // 坐标转换

                    tempNextPoint.x = (nextPoint.x - offset.x) * radius;

                    tempNextPoint.y = (nextPoint.y - offset.y) * radius;

                    

                    if (![type isEqualToString:@"2"]) {

                        [_pathSubLayer.bezierPath moveToPoint: CGPointMake(tempPoint.x, tempPoint.y)];

                        if (![typeNext isEqualToString:@"0"]) {

                            [_pathSubLayer.bezierPath addLineToPoint: CGPointMake(tempNextPoint.x, tempNextPoint.y)];

                        }

                    

                    }

                  

                    if (![type isEqualToString:@"3"] ) {

                        [currentFloorLayer.bezierPath moveToPoint: CGPointMake(tempPoint.x, tempPoint.y)];

                        if (![typeNext isEqualToString:@"0"]) {

                            [currentFloorLayer.bezierPath addLineToPoint: CGPointMake(tempNextPoint.x, tempNextPoint.y)];

                        }

                    }

                    

                }

            }

            idx++;

        }

        

        dispatch_async(dispatch_get_main_queue(), ^{

         

            

            // 先添加宽轨迹图层(底层)

            for (LCLaserPathSubLayer *floorLayer in self.pathFloorLayers) {

                if (!floorLayer.didAddSubLayer) {

                    [self.layer addSublayer:floorLayer];

                    floorLayer.didAddSubLayer = YES;

                }

                floorLayer.path = [floorLayer.bezierPath CGPath];

            }

            

            // 然后添加细轨迹图层(上层)

            if (!_pathSubLayer.didAddSubLayer) {

                [self.layer addSublayer:_pathSubLayer];

                _pathSubLayer.didAddSubLayer = YES;

            }

            

            if(points.count > 1500 && points.count < 10000) {

                _pathSubLayer.lineWidth = [self pathWidth];//[self pathWidth];

                _pathSubLayer.shouldRasterize = NO;

            } else if(points.count >= 10000) {

                _pathSubLayer.lineWidth = [self pathWidth];//[self pathWidth];

                _pathSubLayer.shouldRasterize = YES;

            }else {

                _pathSubLayer.lineWidth = 1.2;

                _pathSubLayer.shouldRasterize = NO;

            }

            

            // 必须设置的参数

//            _pathSubLayer.shouldRasterize = YES;

            _pathSubLayer.rasterizationScale = [UIScreen mainScreen].scale;

              

              // 关键:设置光栅化内容的边界和锚点

            _pathSubLayer.contentsScale = [UIScreen mainScreen].scale;

            _pathSubLayer.needsDisplayOnBoundsChange = YES;

              

              // 优化绘制性能

            _pathSubLayer.drawsAsynchronously = YES;

            _pathSubLayer.allowsEdgeAntialiasing = YES;

              

              // 防止系统自动清理缓存

              if ([_pathSubLayer respondsToSelector:@selector(setContentsFormat:)]) {

                  // iOS 10+ 使用优化的内容格式

                  if (@available(iOS 10.0, *)) {

                      _pathSubLayer.contentsFormat = kCAContentsFormatRGBA8Uint;

                  }

              }

//            _pathSubLayer.allowsEdgeAntialiasing = YES;

//            // 其他优化设置

//            _pathSubLayer.drawsAsynchronously = YES; // 异步绘制,提高性能

//            _pathSubLayer.shouldRasterize = YES;

//            _pathSubLayer.rasterizationScale = [UIScreen mainScreen].scale;

//

            _pathSubLayer.strokeColor = [UIColor whiteColor].CGColor;

            _pathSubLayer.path = [_pathSubLayer.bezierPath CGPath];

            

            dispatch_semaphore_signal(self.semaphore);

            

#ifdef DEBUG

            NSLog(@"轨迹绘制结束  path: %f ms count: %d", (CFAbsoluteTimeGetCurrent() - start) * 1000, points.count);

#endif

        });

    });

}

// 计算floor layer的线宽

- (CGFloat)calculateFloorLineWidthForPointsCount:(NSInteger)count {

    if(count > 1000 && count < 6000) {

        return pathLayerFWidth;

    } else if(count > 6000) {

        return pathLayerFWidth;

    } else {

        return pathLayerFWidth;

    }

}

 //缩放地图时,保持路径宽度为原始视觉宽度

 - (void)setZoomScale:(CGFloat)scale {

     if (scale >= 1.0) {

         _scale = scale;

         [CATransaction begin];

         [CATransaction setDisableActions:true];

         for (NSString *type in self.layerMap) {

             LCLaserPathSubLayer *layer = self.layerMap[type];

//             layer.lineWidth = [self pathWidth];

             layer.lineWidth = pathLayerWidth;

         }

         [CATransaction commit];

     }

 }

- (CGFloat)pathWidth {

    CGFloat width = self.mapConfig.kMinPathWidth;

    CGFloat pixelScale = [[UIScreen mainScreen] scale];

    CGFloat mapScale = 1;

    if (self.mapConfig) {

        mapScale = self.mapConfig.orignalScale * self.scale * pixelScale;

        if (mapScale <= self.mapConfig.minZoomScale) {

            width = self.mapConfig.kMinPathWidth;

        } else if (mapScale >= self.mapConfig.maxZoomScale) {

            width = self.mapConfig.kMaxPathWidth;

        } else {

            CGFloat scaleRange = self.mapConfig.maxZoomScale - self.mapConfig.minZoomScale;

            CGFloat widthRange = self.mapConfig.kMaxPathWidth - self.mapConfig.kMinPathWidth;

            width = mapScale / scaleRange * widthRange + self.mapConfig.kMinPathWidth;

        }

        

        //因为已经tranform缩放过,所以要除回去

        width = width / self.scale * 0.8;

    }

    //NSLog(@"caffe0421 路径宽度: %f 像素宽度:%f", width, width * self.scale * pixelScale);

    return width;

}

#pragma mark - Setter

- (void)setPathModel:(LCLaserPathModel *)pathModel

{

    if (_pathModel) {

        if (_pathModel.pathId != pathModel.pathId) {

            [self clearLayer];

        }

    }

    

    _pathModel = pathModel;

    _pathPoints = _pathModel.pointArr;

}

@end

http://www.dtcms.com/a/606883.html

相关文章:

  • 四川网站建设设计公司排名网站托管费用 优帮云
  • 亚马逊玩具合规新规深度解析:跨境卖家成本控制与合规落地指南
  • 本地服务网站开发惠州市 网站开发公司
  • 淘宝网站建设教程视频教程潍坊网站开发招生信息
  • 网站建设j介绍ppt电子游戏设计方案
  • iOS在制作framework时,oc与swift混编的流程及坑点!
  • 使用wrangler发布cf的workers项目
  • 如东网站制作网站建设工具哪家好
  • 零知IDE——基于STM32F103RBT6和SHT40温湿度传感器的环境监测系统
  • 建立手机个人网站福田企业网站优化有用吗
  • C语言反编译 | 如何高效实现C语言程序反编译及相关技术解析
  • 佛山网站建设过程做游乐设施模型的网站
  • 网站建设培训珠海招商广告
  • nginx wordpress 目录 伪静态seo北京公司
  • C++ 三分查找:在单调与凸函数中高效定位极值的算法
  • wordpress建站教程 cms浙江信息港
  • C++备忘录模式:优雅实现对象状态保存与恢复
  • 网站的数据库选择网页价格表
  • react的框架UmiJs(五米)
  • PySide6/PyQt信号总线的实现、使用以及使用建议
  • 制作网站参考wordpress设置摘要还是显示全文
  • 2012年网站设计方法网站建设吉金手指排名15
  • 二十九、STM32的USART (串口发送)
  • dw做网站链接数据库渭南 网站建设
  • 站长工具查询网站信息1 建设网站目的是什么意思
  • 企业网站开发课程的能力应用图片直链在线生成网站
  • 设计模式学习(3)-行为型模式
  • Angular 2 架构:深度解析与最佳实践
  • 09-微服务原理篇(XXLJOB-幂等-MySQL)
  • 代码随想录训练营打卡Day45| 动态规划part12