ThingJS_Inga

ThingJS_Inga 查看完整档案

北京编辑东北财经大学  |  计算机科学与技术 编辑优锘科技  |  运营经理 编辑 www.thingjs.com 编辑
编辑

让人们更好地认知、管理和创造数字化新世界

个人动态

ThingJS_Inga 发布了文章 · 9月24日

ThingJS把热力图技术引入3D可视领域

物联网数据分析支持通过热力图和点聚合的方式展示设备分布和属性等信息。ThingJS把热力图技术引入可视化领域,支持轻量化开发!

热力图是地理位置可视化的一种表现方式,能够使得比平均发生概率更高的区域能够浮现出来,例如高犯罪区、高交通事故区、高仓库区域。

百度地图和高德地图的JavaScript API都提供了热力图的绘制方法,都是将热力图作为新的图层,叠加到地图上。注意热力图数据源的格式与FeatureLayer(要素图层)相同,都是json数据格式。

首先引用地图组件脚本,创建TileLayer并作为图块图层添加到地图。TileLayer是ThingJS API公开的缓存地图服务,平铺的图层被缓存后,渲染速度更快,发布后使用提供的url地址就可以在代码中调用并在地图上显示和编辑。

// 引用地图组件脚本
THING.Utils.dynamicLoad(['https://www.thingjs.com/uearth/uearth.min.js'], function () {

  var map = app.create({
    type: 'Map',
    style: {
      night: false
    },
    attribution: 'Google'
  });
  var tileLayer1 = app.create({
    type: 'TileLayer',
    id: 'tileLayer1',
    url: 'http://mt0.google.cn/vt/lyrs=s&x={x}&y={y}&z={z}'
  });
  map.baseLayers.add(tileLayer1);

接下来就设置今天的主角“热力图”,官方图层命名HeatMayLayer.
1.jpg

实现原理

热力图实现过程就是通过简单的数学变化,将离散的点信息映射到最终图像上的过程。从地图上看热力图,都是一个个离散点信息,引入地图组件脚本map,作为最终热力图像产生影响的区域。
将所有离散点Map进行叠加,产生一幅灰度图像。离散点密度越高的地方,灰度图中像素点数值越高,即图像越亮。valueField代表用来生成热力图使用的权重字段,不传的话所有点的权重相同,如果传,则从数据的properties中读取该字段的值作为权重值。
最后将生成的灰度图映射到彩色图像上,官方映射关系设置如下:

官方示例(部分)如下:

   $.ajax({
     type: 'GET',
     url: 'https://www.thingjs.com/uearth/res/beijing-POIs-3211.geojson',
     dataType: 'json',
     success: function (data) {
       app.camera.earthFlyTo({
         time: 2000,
         lonlat: [116.44474497103292, 39.9118522313402],
         height: 5000,
         pitch:80,
         complete: function () {
           var layer = app.create({
             type: 'HeatMapLayer',
             dataSource: data, //数据源 geojson格式
             valueField: config.valueField, //权重字段
             needsUpdate: config.needsUpdate, //是否随相机的变化重新绘制热力图
             renderer: {
               radius: config.radius, // 影响半径
               minOpacity: config.minOpacity,//最小值的透明度
               maxOpacity: config.maxOpacity,//最大值的透明度
               mosaic: config.mosaic,//是否使用马赛克效果
               mosaicSize: config.mosaicSize,//马赛克效果的像素值
               gradient: gradientObj[config.gradient] //色带
             },
           });
           map.addLayer(layer);
         }
       });
     }
   });
 }
});

ThingJS,你的轻量化3D开发伙伴!

查看原文

赞 0 收藏 0 评论 0

ThingJS_Inga 发布了文章 · 9月24日

ThingJS把这种天空盒技术封装,实现不到20行代码!

天空盒是应用于场景的背景,以显示天空、空间或封闭结构的纹理。ThingJS把这种天空盒技术封装,实现不到20行代码!

引用地图组件脚本之后地球相机参数就改变,需要校正天空盒。为什么偏偏是天空盒呢?这就得问一下,天空盒的原理是什么?OpenGL中天空盒的思想就是绘制一个大的立方体,然后将观察者放在立方体的中心,当相机移动时,这个立方体也跟着相机一起移动,这样相机就永远不会运动到场景的边缘。

image

所以在实时渲染中,因为照相机随着物体一起移动,在肉眼看来,物体大小几乎是没什么变化的,这种就是天空盒技术玩的把戏。

为了保持视角一致,需要校正天空盒,摄像机飞行完之后,当前的视角变化了,天空盒就需要校正——获取自定义图层tilelayer1的类型、名称和URL,添加此图层到基础的地图集合列表即可。

**THING**.widget.Button('飞到北京', function () {

 app.camera.earthFlyTo({

 lonlat:[116.46429991746982, 39.98638104131414],

 height:5000,

 pitch:15,

 complete:function (){

//飞完根据当前视角校正天空盒

CMAP.Util.correctSkyBox();

var tileLayer1 = app.create({

 type: 'TileLayer',

 name: 'tileLayer1',

 url: 'https://mt{0,1,2,3}.google.cn/vt/lyrs=s&hl=zh-CN&gl=cn&x={x}&y={y}&z={z}'

 });

 map.baseLayers.add(tileLayer1);``````

地球上使用天空盒又有另一套简单的技术实现。所谓的天空盒其实就是将一个立方体展开,然后在六个面上贴上相应的贴图,官方提供多达8种彩色背景gif贴图,比如银河、蓝天、黑夜,可以供不同场景使用。

先引用地图组件脚本,创建完地球相机参数后需校正天空盒,让摄像机始终处于这个立方体的中心位置,就对了。示例如下:

var app = new THING.App({

skyBox: 'MilkyWay' // 设置天空盒为银河git

});

// 引用地图组件脚本

THING.Utils.dynamicLoad(['https://www.thingjs.com/ueart...'], function () {

var map = app.create({

type: 'Map',

backgroundColor: [0, 0, 0],

});

//创建完地球相机参数会改变,需校正天空盒

CMAP.Util.correctSkyBox();

var tileLayer1 = app.create({

type: 'TileLayer',

name: 'tileLayer1',

url: 'https://mt{0,1,2,3}.google.cn/vt/lyrs=s&hl=zh-CN&gl=cn&x={x}&y={y}&z={z}'

});

map.baseLayers.add(tileLayer1);

查看原文

赞 0 收藏 0 评论 0

ThingJS_Inga 发布了文章 · 9月22日

CSS样式如何被ThingJS平台轻松引用?

React 对 CSS 封装非常简单,就是沿用了 DOM 的 style 属性对象,DOM在ThingJS技术中占有极其重要的位置。

HTML绘制平面图形可以采用canvas标签,但是功能上只能呈现2D图形,想要呈现3D图像需要特殊上下文,于是引入了webGL技术。如果想看它的场景演示,可以访问谷歌团队Data Arts出品的基于webGL的3D场景库,前端技术圈也视它为一个蓝海技术,3D开发应用普及指日可待。

国内的物联网可视化技术厂商ThingJS纯JS语法开发,3D效果也不输于原生应用,一切源于谷歌浏览器对webgl技术的支持。从开发生态的角度,webGL技术可以调用显卡、调用麦克风、调用摄像头等一切能用的硬件去提升服务质量,同时也支持引用js脚本和css演示,让你的3D开发效果更加有特色。

官方如何引入外部资源呢?注意在平台新建或者上传文件仅允许js, css, html, json格式。

我们默认js脚本和css样式会带上时间戳,且按urls数组中的顺序加载,浏览器会缓存之前的js,css的版本,我们更新了js,css文件后,浏览器不会更新。所以我们在引入相关css、js文件时使用时间戳,能够让浏览器加载我们的最新版本。如下所示。

THING.Utils.dynamicLoad([
    '/static/vendor/twitter-bootstrap/3.3.7/css/bootstrap.min.css',
    '/static/vendor/twitter-bootstrap/3.3.7/js/bootstrap.min.js',
    '/static/vendor/moment/moment.js'],
function () {

twitter-bootstrap, moment均为外部资源,因网络原因拷贝到了thingjs网站目录。
这样在平台上引用,开发不因为访问不到资源或者访问报错而拖延,所以我们支持css库,JS库打包并提供官方文件夹储存资源,确保稳定,也方便用户无论何时何地都可开发。
ThingJS官方平台支持外部资源,可操作js, css文件新建或者icon、jpg、png、gif、js、css、html、json、ttf、woff、gltf、rvt、ifc、mp3格式的文件上传。

完整运行代码示例如下。

THING.Utils.dynamicLoad([
    '/static/vendor/twitter-bootstrap/3.3.7/css/bootstrap.min.css',
    '/static/vendor/twitter-bootstrap/3.3.7/js/bootstrap.min.js',
    '/static/vendor/moment/moment.js'],
function () {
    // 创建App
    var app = new THING.App({
        url: 'https://www.thingjs.com/static/models/storehouse'
    });
        // 加载场景后执行
    app.on('load', function (ev) {
        var btn = createButton();
        btn.on('click', function () {
            // 使用 moment 库获取当前时间
            var now = moment().format('YYYY-MM-DD HH:mm:ss');
            console.log(now);
        })
    });
})

function createButton() {
    // 使用 bootstrap 样式
    var template =
        `<button class="btn btn-default" type="button" style="position:absolute;left:20px;top:20px;z-index:2">当前时间</button>`;
    var btn = $('#div2d').append($(template));

    return btn;
}

ThingJS主要为物联网小伙伴提供3D开发工具,webgl会越来越原生,3D可视化效果越来越好啦!

查看原文

赞 0 收藏 0 评论 0

ThingJS_Inga 发布了文章 · 9月21日

ThingJS与高德路径规划合作方案新鲜出炉!

地图在城市大屏展示中扮演着必不可缺的角色。想展示动态路径?ThingJS与高德路径规划合作方案新鲜出炉!

CMap 是基于 ThingJS 实现的地图组件库,我们与高德地图导航服务合作开发导航功能,用到其中的路径规划服务,这里的web服务API对所有用户开放,可以轻松开发。
在这里插入图片描述

按照高德的路径规划结果,使用GCJ02坐标系的谷歌影像,导航支持驾车、骑行与步行等交通方式,当然您可以自行开发更多的出行方式,记得使用API前先获取key:
https://lbs.amap.com/api/webs...
在这里插入图片描述

高德地图路径规划服务API是一套以HTTP形式提供的步行、公交、驾车查询及行驶距离计算接口,返回JSON 或 XML格式的查询数据,用于实现路径规划功能的开发。适用场景包括线路查询,以线路结果页面形式展现换乘方案。根据返回线路数据,自行开发线路导航。

开发示例提供起点、终点的按钮设置,根据不同交通方式来设定线路。点击起点按钮,则在地图上单击某处作为起点,终点按钮也是如此。如上图所示。

ThingJS与高德路径导航的开发示例如下:

var app = new THING.App();
// 设置app背景为黑色
app.background = [0, 0, 0];

// 高德地图key 免费版本有次数限制,此处仅供demo使用,如有需要请自行到高德地图网站申请商业版key
var amapKey = '5791cdaf02f4d44fd979a9f89739d06c';

THING.Utils.dynamicLoad(['https://www.thingjs.com/uearth/uearth.min.js'],
    function () {
        var startCoord, endCoord;
        var map = app.create({
            type: 'Map',
            attribution: 'Google'
        });
        var tileLayer1 = app.create({
            type: 'TileLayer',
            id: 'tileLayer1',
            url: 'https://mt{0,1,2,3}.google.cn/vt/lyrs=s&hl=zh-CN&gl=cn&x={x}&y={y}&z={z}'
        });
        map.addLayer(tileLayer1);

        // 创建一个图层展示起点终点的图标以及导航结果
        var thingLayer = app.create({
            type: 'ThingLayer',
            name: 'thingLayer'
        });
        map.addLayer(thingLayer);
        // 飞到地球上某一个位置
        app.camera.earthFlyTo({
            lonlat: [116.4365, 39.97479],
            height: 6000,
            complete: function () {
                createUI();
            }
        });

        // 是否点击选择起点按钮
        var selectStart = false;
        // 是否点击选择终点按钮
        var selectEnd = false;
        // 导航方式选择的UI
        var radio;

        /**
         * @param orgin 起点坐标
         * @param destination 终点坐标
         * @param transport 交通方式
         */
        function nav(origin, destination, transport) {
            // 先清除导航结果
            thingLayer.query('.GeoLine').destroy();
            // 构建查询url 不同出行方式构建url的方式不同 具体请参考高德路径规划api
            var navUrl = '?origin=' + origin + '&destination=' + destination + '&key=' + amapKey;
            var drivingUrl = 'https://restapi.amap.com/v3/direction/driving';
            var bicyclingUrl = 'https://restapi.amap.com/v4/direction/bicycling';
            var walkingUrl = 'https://restapi.amap.com/v3/direction/walking';
            if (transport === '驾车') {
                navUrl = drivingUrl + navUrl;
            }
            else if (transport === '骑行') {
                navUrl = bicyclingUrl + navUrl;
            }
            else if (transport === '步行') {
                navUrl = walkingUrl + navUrl;
            }
            // 请求高德地图导航服务
            $.ajax({
                type: 'GET',
                url: navUrl,
                dataType: 'json',
                success: function (data) {
                    // 先判断是否成功
                    if (data.status === '1' || data.errcode === 0) {
                        var path;
                        // 不同交通方式返回接口结构不同,具体请参考高德路径规划api
                        if (transport !== '骑行') {
                            path = data.route.paths[0];
                        }
                        else {
                            path = data.data.paths[0];
                        }
                        var distance = path.distance;
                        var duration = path.duration;
                        var steps = path.steps;
                        var coordinates = [];
                        for (var i = 0; i < steps.length; i++) {
                            var polyline = steps[i].polyline;
                            var coords = polyline.split(';');
                            for (var j = 0; j < coords.length; j++) {
                                var coord = coords[j].split(',');
                                coordinates.push([parseFloat(coord[0]), parseFloat(coord[1])]);
                            }
                        }
                        // 将路径规划结果创建一个GeoLine对象,并添加到图层
                        var road = app.create({
                            type: 'GeoLine',
                            name: 'road' + i,
                            coordinates: coordinates,
                            renderer: {
                                type: 'image',
                                lineType: 'Plane',
                                color: [255, 0, 0],
                                imageUrl: 'https://www.thingjs.com/uearth/uGeo/path.png',
                                // numPass: 6,
                                width: 6,
                                effect: true,
                                speed: 0.1
                            }
                        });
                        thingLayer.add(road);
                        // 飞到GeoLine对象
                        app.camera.earthFlyTo({
                            object: road
                        });
                    }
                }
            });
        }

        // 给地图添加点击事件,点击地图时选择起点或终点,并在地图上添加一个GeoPoint
        map.on('click', function (e) {
            if (selectStart) {
                startCoord = e.coordinates.toString();
                selectStart = false;
                document.body.style.cursor = 'default';
                var geoPoint = app.create({
                    type: 'GeoPoint',
                    name: 'startPoint',
                    coordinates: e.coordinates,
                    renderer: {
                        type: 'image',
                        url: 'https://www.thingjs.com/uearth/uGeo/start.png',
                        size: 3
                    }
                });
                thingLayer.add(geoPoint);
            }
            if (selectEnd) {
                endCoord = e.coordinates.toString();
                selectEnd = false;
                document.body.style.cursor = 'default';
                var geoPoint = app.create({
                    type: 'GeoPoint',
                    name: 'endPoint',
                    coordinates: e.coordinates,
                    renderer: {
                        type: 'image',
                        url: 'https://www.thingjs.com/uearth/uGeo/end.png',
                        size: 3
                    }
                });
                thingLayer.add(geoPoint);
                if (startCoord !== undefined && endCoord !== undefined) {
                    // 获取当前radio选中的值
                    var transport = radio.getValue();
                    nav(startCoord, endCoord, transport);
                }
            }
        });

        // 创建UI界面
        function createUI() {
            // 创建选择起点按钮
            new THING.widget.Button('选择起点', function () {
                selectStart = true;
                document.body.style.cursor = 'pointer';
                thingLayer.query('.GeoPoint').destroy();
                thingLayer.query('.GeoLine').destroy();
            });
            // 创建选择终点按钮
            new THING.widget.Button('选择终点', function () {
                if (selectStart) {
                    return;
                }
                selectEnd = true;
                document.body.style.cursor = 'pointer';
            });
            // 创建一个配置界面组件
            var panel = new THING.widget.Panel({
                titleText: '交通方式',
                hasTitle: true,
                width: 150

            });
            panel.positionOrigin = 'TR';// top-right
            panel.position = ['100%', 0];
            // 添加 单选框 组件
            radio = panel.addRadio({ 'radio': '驾车' }, 'radio', ['驾车', '骑行', '步行']);
            // 监听单选框选项改变的事件
            radio.on('change', function (ev) {
                nav(startCoord, endCoord, ev)
            })
        }
    });

ThingJS支持地图在线进行二次开发和分享。

查看原文

赞 0 收藏 0 评论 0

ThingJS_Inga 发布了文章 · 9月18日

3D技术开花结果,ThingJS为更智慧的人居生活贡献绵薄之力

目前形形色色的智慧城市应用不仅提升了城市的运行效率和生产力,也让它变得更灵敏、更宜居。ThingJS深耕3D可视化领域,为更智慧的人居生活贡献绵薄之力。
第二十三届中国北京国际科技产业博览会,简称科博会,昨天在北京的中国国际展览中心开展了,这一次优锘科技也上了线上展播的头条,在数字化城市领域占得一席。欢迎查看ThingJS智慧城市项目,感受一下优锘ThingJS团队的技术创新能力。
image

有必要再介绍一遍,ThingJS主要是为js开发者提供一套3D开发组件,包括ThingJS开发、CityBuilder, CamBuilder以及全景图等,站在3D创业轻量化的角度,把3D开发流程简化成四个步骤:场景搭建—在线开发—数据对接—项目部署,如果是做一个简单的3D可视化演示项目,js工程师仅需7天就可以创建自己的3D项目,致力于把项目成本降低,让3D可视技术普及大众。

麦肯锡研究院研究数据表明,数字技术是打造美好城市的一项工具,好的智慧应用可将城市生活质量的指标提高10-30%。“打造智慧城市”不是目标,而是手段,目的是为了更高效、更动态响应居民或员工的需求和期望,从而避免更多的危险或麻烦。因此技术不是第一位的,技术背后的人更重要。简单、颜值高、轻松开发不仅仅是ThingJS普惠3D可视化的需要,也符合人人参与智慧城市建设的愿望。
在这里插入图片描述
小科普:一个有生气的智慧城市由三个层面协同打造(见上图)。第一层是技术基础,包括大量连入高速通讯网络的智能手机和传感器,以及多个开放式数据平台。传感器会持续采集交通流量、能耗、空气质量等变量的读数以及日常生活中的其他数据,并将这些信息直接推送到需求者的指尖。
第二层是具体智能应用层。原始数据必须经过处理才能转化为示警、洞见和行动,而开发这些应用则是技术提供商和App开发人员的职责。常见的智能应用可分为八大类:安全、出行、健康、能源、水、垃圾、经济发展和住宅、参与和社区。ThingJS主要针对物联网3D场景开发所用的工具组件,适用行业范围较广。
第三层是城市、企业和公众的接纳及使用程度。许多应用只有在广泛普及并改变了人们的行为时,才算是取得了成功。它们向个人用户提供更透明的信息,让其获得全局视角,以便做出更好的决策。例如一个城市中,幸福指数的考虑维度之一便是安全性。我们会经常遇到暴雨、雪灾甚至失火等危机情况,应急响应一直是城市管理的重要功能之一,尤其是在生死攸关时刻,分秒必争,因此必须尽可能缩短现场急救员到达火灾等事故现场的时间。
这也成为3D城市可视化项目的常见需求,就是模拟下雨,下雪的天气,有时也会模拟爆炸,着火等效果。例如模拟着火效果:
在这里插入图片描述
在这里插入图片描述
ThingJS可以基于JS开发场景粒子效果。真相是,系统发射了很多小面片,这些小面片贴上图,再配合上旋转,缩放等模拟出各种需要的效果。
在这里插入图片描述
这些效果ThingJS使用粒子系统(particle)技术即可实现,官方示例如下:
`/**
var app = new THING.App({

url: 'https://www.thingjs.com/static/models/storehouse'     // 场景地址

});

app.on('load', function (ev) {

var car = app.query('car01')[0];

// 创建火焰粒子
new THING.widget.Button('create', function () {
    var fire = app.query('#fire01')[0];

    if (!fire) {
        app.create({
            id: 'fire01',
            type: 'ParticleSystem',
            url: 'https://model.3dmomoda.com/models/19061018snbajhvuzrheq9sbgwdoefuk/0/particles',
            parent: car, // 设置粒子的父物体
            localPosition: [0, 0, 0] // 设置粒子相对于父物体的位置
        });
    }
})

// 销毁粒子
new THING.widget.Button('destroy', function () {
    var fire = app.query('#fire01')[0];
    if (fire) {
        fire.destroy();
    }
})`

ThingJS让3D可视技术颜值更高,智慧城市生活体验更好!

查看原文

赞 0 收藏 0 评论 0

ThingJS_Inga 发布了文章 · 9月17日

ThingJS教你创建一个逃不掉的多边形区域

也许命运就是一次封闭曲线的行走,无论怎么走都是一个多边形。ThingJS教你创建一个逃不掉的多边形区域,js语法实现超简单。
1.jpg

矩形区域和圆形区域都很好画,大部分建模工具可直接拖拽生成,但多边形区域则需要确定多边形要经过的点,得出多边形的最终形状。

ThingJS绘制多边形区域,以矩形和圆形为例,效果展示如下图。
2.jpg

ThingJS在线平台通过获取多个点坐标来确定多边形的形状,例如矩形获取4个世界坐标系下的坐标组合,构成多边形或矩形的点。

 var points = [[0, 0, 0], [5, 0, 0], [5, 0, 5], [0, 0, 5]];

如果是圆半径,获取圆半径和圆心世界坐标后,需要根据圆形和半径计算坐标点。

var radius = 3;// 圆半径
    var center = [10, 0, 0]; // 圆心世界坐标
    // 根据圆形和半径计算坐标点
    var points = [];
    for (var degree = 0, y = 0; degree <= 360; degree += 10) {
        var x = Math.cos(degree * 2 * Math.PI / 360) * radius;
        var z = Math.sin(degree * 2 * Math.PI / 360) * radius;
        var pos = THING.Math.addVector([x, y, z], center);
        points.push(pos);
    }    

创建区域需要定义一个type属性PolygonRegion,传入点坐标并按照自己喜欢的样式,设置区域颜色、边框颜色或不透明度等。

完整官方示例供参考。

/**
 * 说明:创建多边形区域
 */
var app = new THING.App({
    url: 'https://www.thingjs.com/static/models/storehouse'     // 场景地址
});

new THING.widget.Button('创建区域', function () {
    // 构成多边形的点(取世界坐标系下的坐标)
    var points = [[0, 0, 0], [5, 0, 0], [5, 0, 5], [0, 0, 5]];

    // 创建区域
    var region = app.create({
        type: 'PolygonRegion',
        points: points, // 传入世界坐标系下点坐标
        style: {
            regionColor: '#ff0000', // 区域颜色
            lineColor: '#00ff00', // 边框颜色
            regionOpacity: 1 // 不透明度 (默认是 0.5 半透明)
        }
    })
})

new THING.widget.Button('创建圆形区域', function () {
    var radius = 3;// 圆半径
    var center = [10, 0, 0]; // 圆心世界坐标
    // 根据圆形和半径计算坐标点
    var points = [];
    for (var degree = 0, y = 0; degree <= 360; degree += 10) {
        var x = Math.cos(degree * 2 * Math.PI / 360) * radius;
        var z = Math.sin(degree * 2 * Math.PI / 360) * radius;
        var pos = THING.Math.addVector([x, y, z], center);
        points.push(pos);
    }

    // 创建区域
    var region = app.create({
        type: 'PolygonRegion',
        points: points, // 点坐标
        style: {
            regionColor: 'rgb(0,0,255)', // 区域颜色
        }
    })
})

new THING.widget.Button('删除', function () {
    var regions = app.query('.PolygonRegion');
    regions.destroy();
})

ThingJS教你3D开发,做出甲方爸爸赞不绝口的项目!

查看原文

赞 0 收藏 0 评论 0

ThingJS_Inga 发布了文章 · 9月16日

ThingJS技术实现UV动画,场景开发组件唾手可得

模型顶点对应的UV对应纹理像素不断随时间变化。但是无法达到让物体“变形”的效果,只能是模拟重复的背景。ThingJS技术实现UV动画,场景开发组件唾手可得!
UV是什么?通过改变纹理坐标,实现动态效果的纹理动画。在3D场景中,一些动态水面、流动的岩浆、跳动的火焰等等,都是通过操作UV做的动画。在CampusBuilder可以根据功能项的曲线属性设置动画效果,修改流动速度、线宽大小、贴图位移、贴图重复等样式。
下载CamBuilder编辑器,免费注册账号,在右侧菜单栏找到“功能”一项。
1.jpg

在CampusBuilder中,选择功能项下的曲线,手动添加一条曲线,线宽和锚点可以手动设置,一个锚点可生成一个新的片段。
2.jpg

选择一张贴图,刷到曲线中,生成背景画面。
3.jpg

点击曲线属性,选择开启流动动画,利用流动速度的正负设置正反方向,数字越大代表速度越快。CamBuilder可以无缝对接ThingJS在线平台,开启曲线UV动画示例代码如下:

var app = new THING.App({ 
    // 场景地址
    "url": "https://www.thingjs.com/static/models/guan"
});

app.on('load', function () {
    app.query('.RouteLine')[0].scrollUV = true;
    app.query('.RouteLine')[0].scrollSpeed = -100;
})

4.jpg
5.jpg

什么是锚点呢?片断是由锚点组成的,只有修改锚点才能改变片断形态。注意锚点一旦被删除,片段也会同时消失。在CamBuilder内,点击左键可以设置新锚点,可能因操作原因产生重叠锚点现象,虽然不影响流动动画,但是会缺乏流畅度,如上图示。

ThingJS有最受前端工程师欢迎的3D场景开发组件!

查看原文

赞 0 收藏 0 评论 0

ThingJS_Inga 发布了文章 · 9月15日

ThingJS提供对塌陷建模之3D对象开发与控制

3D常用到塌陷工具,以释放内存使电脑更加流畅,不过塌陷后的模型将不具备原始的修改参数,这都被整合了。ThingJS提供对塌陷后建模的3D对象开发与控制。

今天来讲讲如何基于js语法来开发一个物体模型拆解展开的效果,专业名称叫“物体爆炸图”,标准ThingJS体系模型出于互动模型性能考虑,都要求在模型上传前做塌陷,这种建模细节对于提升3D开发效率很有必要,目的是减少模型对内存的占用。

在3D开发之前,模型本身会重叠很多命令,占用很大一部分内存和CPU,拖慢电脑,所以针对模型进行塌陷后(指的是把很多个物体合并成一个,转换为一个命令可编辑多边形或可编辑网格),就会去除这些多余的命令参数,不再花时间记录和存储,从而加快运行速度。

制作物体模型时,根据爆炸图中各个零件的拆分需要,针对子模型或子节点定义并命名物体子对象,在3DMAX等建模软件里就能创建子对象。这些子对象在ThingJS在线开发中可作为模型子节点来控制,能够像单独模型对象物体一样进行移动、添加事件等操作。

拆分后磨性子节点如果有多材质或点数超过上限,那在ThingJS开发中会继续拆分,并在子节点中被命名成组,组内继续拆分“01”、“02”等对象。例如:3dmax命名, 一个子节点名字为“box”,由于该子节点使用了多种材质,该子节点在在线开发中会被命名成组,组内会被拆分并命名为“box_0”,“box_1”等对象。

注意经塌陷的模型不再有子节点保留,只有上述分项控制模型局部要求,保留已命名的子对象信息,最大程度上提高开发性能,又满足模型拆分的特殊需要。

官方示例请各位看官参考:

// 加载场景代码 
var app = new THING.App({ 
    url: '/api/scene/406e419fae9000a47a4a8899'
});

// 发电机模型节点数据
var nodeObjData = {
    '1': {name: '机座', offset: [0, 0, -1]},
    '2': {name: '保护装置', offset: [0, -1, 0]},
    '3': {name: '电瓶', offset: [0, -1, 0]},
    '4': {name: '排气口', offset: [0, 0, 1]},
    '5_0': {name: '过滤器', offset: [0, 0, 1]},
    '5_1': {name: '过滤网', offset: [0.5, 0, 1]},
    '6': {name: '供给装置', offset: [0, 0, 1]},
    '7': {name: '烟囱', offset: [-1, 0, 0]},
    '8': {name: '发电机'},
    '9': {name: '控制器', offset: [0, 1, 0]}
}
// 发电机模型节点对象
var nodeJsonData = null;
// 发电机对象
var generatorObj = null;
// 发电机展开状态
var expandState = false;
// 发电机展开次数
var expandCount = 0;

// 场景加载完成后执行
app.on('load', function (ev) {
    // 查询发电机对象
    generatorObj = app.query('#generator')[0]
    // 获取发电机模型节点对象
    nodeJsonData = getNode(generatorObj);

    // 创建测试按钮
    new THING.widget.Button('展开', expandObj);
    new THING.widget.Button('还原', unexpandObj);
    new THING.widget.Button('顶牌显示', createAllPanel);
    new THING.widget.Button('顶牌隐藏', hiddenAllPanel);
})

/**
 * 说明:显示所有顶牌
 */
function createAllPanel(){
    for (let key in nodeObjData) {
        nodeJsonData[key].name = nodeObjData[key].name;
        createPanel(nodeJsonData[key]);
    }
}

/**
 * 说明:隐藏所有顶牌
 */
function hiddenAllPanel(){
    for (let key in nodeObjData) {
        hiddenPanel(nodeJsonData[key]);
    }
}

/**
 * 说明:展开物体
 */
function expandObj() {
    // 防止发电机在执行一次展开过程中多次点击
    if (expandState) {
        return;
    }
    expandState = true;
    expandCount++;
    for (let key in nodeObjData) {
        // 各子节点进行偏移
        objOffset(nodeJsonData[key], nodeObjData[key].offset);
    }
}

/**
 * 说明:还原物体
 */
function unexpandObj() {
    // 展开次数为0,代表未展开
    if (expandCount == 0) {
        return;
    }
    for (let key in nodeObjData) {
        if(nodeObjData[key].offset){
            // 计算还原时子节点需要进行的偏移量,数值为 -1 * 展开次数 * nodeObjData中定义的该子节点对应的偏移量
            let offsetValue = [-1 * expandCount * nodeObjData[key].offset[0], -1 * expandCount * nodeObjData[key].offset[1], -1 * expandCount * nodeObjData[key].offset[2]]
            objOffset(nodeJsonData[key], offsetValue);
        }
    }
    expandCount = 0;
}

/**
 * 说明:获取节点对象
 */
function getNode(obj) {
    let nodeJson = {};
    // obj.subNodes 即可获取到一个物体的所有子节点
    for (let i = 0; i < obj.subNodes.length; i++) {
        let subnode = obj.subNodes[i];
        // 获取物体子节点对象中node属性的type值,只有当type值为Mesh时,才能对物体添加事件
        let type = subnode.node.type;
        if(type == 'Mesh'){
            nodeJson[subnode.name] = subnode;
        }
    }
    return nodeJson;
}

/**
 * 说明:物体偏移
 */
function objOffset(obj, value) {
    if (!value) {
        return;
    }
    // 物体移动
    obj.moveTo({
        offsetPosition: value,  // 自身坐标系下的相对位置
        time: 500,  // 移动完成需要的时间
        complete: function () {
            expandState = false;
        }
    });
}

/**
 * 说明:创建面板
 */
function createPanel(obj) {
    // 判断是否已经创建过面板,如果已创建,显示,否则创建面板
    var panel = obj.getAttribute('panel');
    if (panel != null) {
        panel.visible = true;
        return;
    }
    // 创建panel
    panel = new THING.widget.Panel({
        // 设置面板宽度
        width: '100px',
        // 没有角标 none ,没有线的角标 noline ,折线角标 polyline
        cornerType: 'polyline'

    })
    // 绑定物体身上相应的属性数据
    panel.addString(obj, 'name').caption('');
    // 创建UIAnchor面板
    var uiAnchor = app.create({
        // 类型
        type: 'UIAnchor',
        // 设置父物体
        parent: obj,
        // 要绑定的dom元素对象
        element: panel.domElement,
        // 设置 localPosition 为 [0, 0, 0]
        localPosition: [0, 0, 0],
        // 相对于面板左上角的偏移像素值,当前用值是角标的中心点
        pivotPixel: [-16, 109]
    });
    // 更改面板文本样式
    $('.ThingJS_wrap .main .ThingJS_UI .ThingJS_string-value').css('text-align', 'center');
    obj.setAttribute('panel', uiAnchor);
}

/**
 * 说明:隐藏面板
 */
function hiddenPanel(obj) {
    var panel = obj.getAttribute('panel');
    if (panel != null) {
        panel.visible = false;
    }
}

ThingJS基于WEBGL的3D框架,纯JS语法搭建,对物联网开发者非常友好!

查看原文

赞 0 收藏 0 评论 0

ThingJS_Inga 发布了文章 · 9月14日

ThingJS提供灯光配置基础方法,节省开发精力!

三维软件可以随意创建任何类型灯光的自由性,但是逼真效果还是需要费一番功夫。ThingJS提供灯光配置基础方法,节省开发精力!
灯光有助于表达一种情感,或引导观众的目光看向具体的位置。灯光能够反映一种基调,对整个图像的外观至关重要。下面教你关于打灯光的技巧,以及轻便的开发方法。
ThingJS提供一套操作简便的灯光配置项,只要配置结束后,记得点击生成代码块并执行,一套完整的初级灯光效果就出来了。
1.jpg

主灯光

主灯光对象是对主光源的控制代码,可设置的变量包括阴影、颜色、强度和打灯角度。主光源通常放在四分之三的位置上,位置是从物体的正面转45度,并从中心线向上转45度。夜晚的场景很适用于单一的主光源,没有其他的自然光,其他的地方黑得不见五指,这正符合单一条件的主光源效果。
### 环境光
在主灯光对象之前,还有对环境光、半球光照的配置。这两种光源均是天然漫反射的光线,在自然环境中这是一种自然的光亮,不至于让整个环境置于黑箱之中。环境光能够提高整个场景的亮度。半球光照特地用来补充地面或建筑角落里的光线,柔化阴影或减少阴影区域,符合物联网可视化场景的需求。

背景光

第二光源对象也需要设置具体的位置和发光对象的颜色,作为主光源的一种补充光源,感觉会更加逼真。事实上,自然的光线很少只有一种光线照明,而是多种颜色的灯光组合,提供一种景深的感觉。
在3DMAX中打灯是一个复杂的技巧,真正学会操作很费时间,相对来说ThingJS就轻量很多,通过提供场景灯光的配置选项,再生成代码块,方便开发人员迅速掌握,层次效果更丰富。
场景灯光通过app.lighting属性设置,难度一星,代码示例如下。

var app = new THING.App({
    url: 'https://www.thingjs.com/static/models/chinesehouse'
});

app.on('load', function (ev) {
    app.camera.position = [18.217000353088125, 16.96513529990897, 11.90387798709408];
    app.camera.target = [-0.92, 2.1, 2.7];
})

// 环境光对象
var ambientLight = {
    intensity: 0.4,
    color: '#FFFFFF',
};
// 半球光照
var hemisphereLight = {
    intensity: 0.5,
    color: '#FFFFFF',
    groundColor: '#202020',
};
// 主灯光对象
var mainLight = {
    shadow: true,
    intensity: 0.6,
    color: '#FFFFFF',
    alpha: 120,
    beta: 0,
};
// 第二光源对象
var secondaryLight = {
    shadow: false,
    intensity: 0,
    color: '#FFFFFF',
    alpha: 0,
    beta: 0,
};
// 全局配置
var config = {
    ambientLight,
    hemisphereLight,
    mainLight,
    secondaryLight
}

new THING.widget.Button('调整场景灯光', function () {
    // 设置灯光
    app.lighting = config;
})

ThingJS让物联网3D创业项目成功率更高!

查看原文

赞 0 收藏 0 评论 0

ThingJS_Inga 发布了文章 · 9月11日

数字孪生众创技术,人人都能用ThingJS

捕获.PNG
在新基建的建设大潮中,物联网已经成为基础设施建设的数字基础,并将以更快速度连接到多样化应用场景,ThingJS平台顺应3D可视化开发需要。

科学界有一种说法,人类真正从二维文明发展到三维文明,其标志性事件其实是人造卫星的升空。3D可视化普及大众,ThingJS的众创能力功不可没。

数字孪生可视化是未来趋势,3D效果则成为追捧对象,2D是平面技术的一种,但是感官体验、空间感与3D截然不同。人的世界是三维视觉,所以3D技术被认为是高度仿真,3D功能增效包括光效、雨雾、动画等,力求逼真效果。

优锘科技独立研发具有自主产权的数字孪生可视化平台和相应的解决方案,更是推出了史诗轻量级在线开发平台——ThingJS,满足小微团队快速开发3D可视化应用需求。数字孪生技术通过将建筑模型、设备模型、物理连接、感测数据和传统的二维图表结合的方式,消除了技术难度上的壁垒,建立了真实世界的数字镜像。

真实场景还原

3D开发中会经常出现一个词:管理对象,在3D空间内我们时时刻刻需要控制对象。先搭建精模真实还原对象外观及位置,再利用ThingJS封装库开发3D仿真效果,最后输出链接供PC端、移动端可访问,实现真实场景还原,常见的场景还原包括环境可视化、设备可视化等。

环境可视化是将目前数据中心机房的物理环境做虚拟仿真,从机房、机柜、机柜内IT设备及数据中心机房的各类基础设施。

设备可视化则是让用户在3D环境中搜索、查看设备的外观和信息。一旦出现故障,三维可视化能够让客户在场景中迅速定位到故障设备的位置,也能够通过物理位置的距离特性进行关联分析。数字孪生技术手段有助于加速定位,故障得以解决。

3D轻量开发

实时巡检或演示汇报等,这些都是数字孪生众创项目的落地动作,ThingJS提供了技术支撑。

通过将数字孪生中特有的摄像机、视角和平滑切换技术整合起来,就形成了动画制作的功能,利用这个功能,用户能够自己动手,把他感兴趣的,任意角度的画面无缝地衔接起来,形成一套完整的动画。这个功能被用户广泛应用在实时巡检和演示汇报上,充分地体现了数字孪生创新理念。

3D开发包括摄像机视角设置,ThingJS官方示例提供摄像机位置、飞到物体和环绕物体的实现方式。如下:

var app = new THING.App({
    url: 'https://www.thingjs.com/static/models/storehouse'
});

// 创建UI
function createUI() {
    new THING.widget.Button('直接设置', set_camera);
    new THING.widget.Button('飞到位置', flytoPos);

    new THING.widget.Button('聚焦物体', fit_camera);
    new THING.widget.Button('飞到物体', flytoObj);
    new THING.widget.Button('环绕物体', rotate_around_obj);
}
createUI();

// 直接设置
function set_camera() {
    // 设置摄像机位置和目标点
    // 可直接利用 代码块——>摄像机——>设置位置
    app.camera.position = [-10.179597135589418, 57.92056475377832, -69.93170920109229];
    app.camera.target = [8.694689127408054, -7.003812939834516, 11.51772904610059];
    // 打印当前摄像机位置 和 目标点
    app.camera.log();
}
// 飞到位置
function flytoPos() {
    // 摄像机飞行到某位置
    // 可直接利用 代码块——>摄像机——>飞到位置
    app.camera.flyTo({
        position: [40.0, 10.0, 25.0],
        target: [8.0, -2.0, 4.0],
        time: 2000,
        complete: function () {
            console.log('飞行结束')
        }
    });
}
// 聚焦物体
function fit_camera() {
    var car = app.query('car01')[0];
    app.camera.fit(car);
}
// 飞到物体
function flytoObj() {
    var car = app.query('car02')[0];
    car.style.color = '#ff0000';
    // 可直接利用 代码块——>摄像机——>飞到物体

    // 摄像机飞行到某物体
    app.camera.flyTo({
        'object': car,
        // 'xAngle': 30,  //绕X轴旋转的角度
        // 'yAngle': 60,  //绕Y轴旋转的角度
        // 'radiusFactor':3,  //物体包围球半径的倍数
        'time': 2 * 1000,
        'complete': function () {
            console.log('飞行结束');
            car.style.color = null;
        }
    });
}

// 环绕物体,围绕car在5秒内旋转180度
function rotate_around_obj() {
    var car = app.query('car01')[0];
    app.camera.rotateAround({
        object: car,
        time: 5000,
        yRotateAngle: 180
    });
}

ThingJS让数字孪生众创技术从小众高端圈子里发迹,人人都可以参与3D开发。

查看原文

赞 0 收藏 0 评论 0

认证与成就

  • 获得 4 次点赞
  • 获得 2 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 2 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 7月15日
个人主页被 167 人浏览