<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
	<channel>
		<title><![CDATA[WINNUM Community - Портал]]></title>
		<link>https://community.winnum.io/</link>
		<description><![CDATA[WINNUM Community - https://community.winnum.io]]></description>
		<pubDate>Mon, 04 May 2026 09:09:15 +0000</pubDate>
		<generator>MyBB</generator>
		<item>
			<title><![CDATA[Щось легке та змістовне українською]]></title>
			<link>https://community.winnum.io/showthread.php?tid=71</link>
			<pubDate>Sun, 03 May 2026 12:39:03 +0300</pubDate>
			<dc:creator><![CDATA[<a href="https://community.winnum.io/member.php?action=profile&uid=223">StacyBoape</a>]]></dc:creator>
			<guid isPermaLink="false">https://community.winnum.io/showthread.php?tid=71</guid>
			<description><![CDATA[Знайшли час для пізнавальний вітчизняний портал? Завітайте на <a href="https://kokl.ua/" target="_blank" rel="noopener" class="mycode_url">kokl.ua</a> – на цьому ресурсі представлені матеріали на різноманітні теми: від корисних порад і лайфхаків до розширених публікацій на теми побуту, самопочуття та вільного часу. Сайт підійде аудиторії, яка шукає просте, змістовне й свіже українською мовою.]]></description>
			<content:encoded><![CDATA[Знайшли час для пізнавальний вітчизняний портал? Завітайте на <a href="https://kokl.ua/" target="_blank" rel="noopener" class="mycode_url">kokl.ua</a> – на цьому ресурсі представлені матеріали на різноманітні теми: від корисних порад і лайфхаків до розширених публікацій на теми побуту, самопочуття та вільного часу. Сайт підійде аудиторії, яка шукає просте, змістовне й свіже українською мовою.]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Каталог перевірених українських новинних сайтів: незалежні джерела, глибока аналітика]]></title>
			<link>https://community.winnum.io/showthread.php?tid=70</link>
			<pubDate>Sat, 02 May 2026 21:57:07 +0300</pubDate>
			<dc:creator><![CDATA[<a href="https://community.winnum.io/member.php?action=profile&uid=222">Michgat</a>]]></dc:creator>
			<guid isPermaLink="false">https://community.winnum.io/showthread.php?tid=70</guid>
			<description><![CDATA[Вітаю всіх! В епоху фейків та дезінформації просто життєво необхідно користуватися якісними джерелами, замість того щоб губитися в неправдивих повідомленнях і поверхневих замітках. Нещодавно натрапив на сайт, що розв'язує цю проблему — він є каталогом перевірених українських ЗМІ. Колекція охоплює як місцеві видання, так і загальнонаціональні аналітичні портали: від новин Буковини до наукових дискусій та фактчекінгу. Зберігайте це посилання, щоб отримувати новини лише з надійних медіа на <a href="https://newandlostdpuie.space/" target="_blank" rel="noopener" class="mycode_url">newandlostdpuie.space</a>]]></description>
			<content:encoded><![CDATA[Вітаю всіх! В епоху фейків та дезінформації просто життєво необхідно користуватися якісними джерелами, замість того щоб губитися в неправдивих повідомленнях і поверхневих замітках. Нещодавно натрапив на сайт, що розв'язує цю проблему — він є каталогом перевірених українських ЗМІ. Колекція охоплює як місцеві видання, так і загальнонаціональні аналітичні портали: від новин Буковини до наукових дискусій та фактчекінгу. Зберігайте це посилання, щоб отримувати новини лише з надійних медіа на <a href="https://newandlostdpuie.space/" target="_blank" rel="noopener" class="mycode_url">newandlostdpuie.space</a>]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Добавление тега в 3D]]></title>
			<link>https://community.winnum.io/showthread.php?tid=69</link>
			<pubDate>Thu, 30 Apr 2026 12:51:59 +0300</pubDate>
			<dc:creator><![CDATA[<a href="https://community.winnum.io/member.php?action=profile&uid=8">Lamantur</a>]]></dc:creator>
			<guid isPermaLink="false">https://community.winnum.io/showthread.php?tid=69</guid>
			<description><![CDATA[Добрый день!<br />
<br />
Если вы хотите добавить в 3Д сцену отображение сигнала, то лучший способ сделать это - через тег.<br />
<br />
Рассмотрим на примере модификации таблички с именем станка для отображения текущего имени запущенной программы.<br />
<br />
Для начала добавим тег к учету, поскольку это уже делается в объекте для работы колб и тех же самых табличек - мы легко сможем улучшить его.<br />
<br />
<br />
WNScriptForCylinders<br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>var currentDate = new Date();<br />
var curDateTimeText = '';<br />
var delay = 0;<br />
var delay_limit = 10000;<br />
var debug = false;<br />
scene.userData.appid = 'winnum.org.app.WNApplicationInstance:1';<br />
var tag_oid;<br />
_getPlatformDateTime_();<br />
function update( event ) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;delay += parseInt(event.delta); <br />
&nbsp;&nbsp;&nbsp;&nbsp;if ( delay &lt; delay_limit ) { return; } else { delay = 0; } //delay for delay_limit<br />
&nbsp;&nbsp;&nbsp;&nbsp;_getPlatformDateTime_();&nbsp;&nbsp;&nbsp;&nbsp;<br />
}<br />
<br />
// Время платформы<br />
function _getPlatformDateTime_(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils.service.WNCalendarHelper.getPlatformDateTime(function(data){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var curDateTime = DateTime_Attr(data);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;currentDate = new Date(curDateTime);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (debug) {console.log('current DateTime Platform:');console.log(curDateTime)};<br />
&nbsp;&nbsp;&nbsp;&nbsp;});<br />
}<br />
<br />
baseSdkUtils._getTag_ = {};<br />
baseSdkUtils._getTag_ = function(tag_name){<br />
&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils.service.WNApplicationHelper.getTag(scene.userData.appid, function(data){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ( baseSdkUtils.isSuccess(data) ){ <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(var i = 0; i &lt; data.getElementsByTagName('item').length ; i++ ) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _temp_item_ = data.getElementsByTagName('item')[i];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _attr_ = baseSdkUtils.decode(_temp_item_.getAttribute('tag'));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (_attr_ == tag_name){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(tag_name == 'NC_ON'){scene.userData.tag_oid1 =  baseSdkUtils.decode(_temp_item_.getAttribute('id'));}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(tag_name == 'NC_PROGRAM_RUN'){scene.userData.tag_oid2 =  baseSdkUtils.decode(_temp_item_.getAttribute('id'));}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(tag_name == 'NC_WIP'){scene.userData.tag_oid3 =  baseSdkUtils.decode(_temp_item_.getAttribute('id'));}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(tag_name == 'NC_FEEDRATE_OVERRIDE'){scene.userData.tag_oid_overfeedrate =  baseSdkUtils.decode(_temp_item_.getAttribute('id'));}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(tag_name == 'NC_ALARM_MESSAGE'){scene.userData.tag_oid_alarmmessage =  baseSdkUtils.decode(_temp_item_.getAttribute('id'));}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// ДОБАВЛЕНО: поиск тега NC_PROGRAM_NAME<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(tag_name == 'NC_PROGRAM_NAME'){scene.userData.tag_oid_programname =  baseSdkUtils.decode(_temp_item_.getAttribute('id'));}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;scene.userData.tag_oid =  baseSdkUtils.decode(_temp_item_.getAttribute('id'));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (debug){console.log('tag_oid for ' + tag_name + ':');console.log(scene.userData.tag_oid)};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;})<br />
}<br />
<br />
function start(event){<br />
&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils._getTag_('NC_ON');<br />
&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils._getTag_('NC_PROGRAM_RUN');<br />
&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils._getTag_('NC_WIP');<br />
&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils._getTag_('NC_FEEDRATE_OVERRIDE');<br />
&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils._getTag_('NC_ALARM_MESSAGE');&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;// ДОБАВЛЕНО: инициализация поиска тега NC_PROGRAM_NAME<br />
&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils._getTag_('NC_PROGRAM_NAME');&nbsp;&nbsp;&nbsp;&nbsp;<br />
}<br />
<br />
baseSdkUtils._getCurrentWorkShiftStartDate_ = {};<br />
baseSdkUtils._getCurrentWorkShiftStartDate_ = function(_poid_, theThis, tag_name, relatedProductLine, relatedProductName){<br />
&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils.service.WNCalendarHelper.getCurrentWorkShiftStartDate(_poid_, function(data){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var startShiftDate = DateTime_Attr(data);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (debug){console.log('start current shift Date:'); console.log(startShiftDate)};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (startShiftDate == 'error'){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(debug){console.log('Нет текущей смены для ' + _poid_)};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;base3DUtils[tag_name][relatedProductLine][relatedProductName] = 0.0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;theThis.scale.y = 0.0001;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var startTime = baseSdkUtils.toDateTimeString(startShiftDate, true);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var endTime = baseSdkUtils.toDateTimeString(currentDate, true);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _tag_oid_ = '';<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(tag_name == 'NC_ON'){_tag_oid_ = scene.userData.tag_oid1 ;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(tag_name == 'NC_PROGRAM_RUN'){_tag_oid_ = scene.userData.tag_oid2 ;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(tag_name == 'NC_WIP'){_tag_oid_ = scene.userData.tag_oid3 ;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils.service.WNApplicationTagHelper.getSimpleTagCalculation(scene.userData.appid, _poid_, _tag_oid_, startTime, endTime, 60000, function(TagCalculation_data){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ( baseSdkUtils.isSuccess(TagCalculation_data) ){ <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(debug){console.log('getSimpleTagCalculation for ' + _poid_ + ' :'); console.log(TagCalculation_data)};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(TagCalculation_data.getElementsByTagName('item').length === 0){return;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(var i = 0; i &lt; TagCalculation_data.getElementsByTagName('item').length ; i++ ) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _temp_item_ = TagCalculation_data.getElementsByTagName('item')[i];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _attr_ = baseSdkUtils.decode(_temp_item_.getAttribute('percent'));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _attr1_ = parseFloat(baseSdkUtils.decode(_temp_item_.getAttribute('hours'))).toFixed(2);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _rel_mesh_ = _attr_/100;<br />
                               &nbsp;&nbsp;if (base3DUtils[tag_name][relatedProductLine]['hour'] === undefined){<br />
                                          base3DUtils[tag_name][relatedProductLine]['hour'] = {};<br />
                                          base3DUtils[tag_name][relatedProductLine]['hour'][relatedProductName] = 0.0;<br />
                                  }              <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;base3DUtils[tag_name][relatedProductLine][relatedProductName] =_rel_mesh_;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;base3DUtils[tag_name][relatedProductLine]['hour'][relatedProductName] =_attr1_;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (_rel_mesh_ &gt; 1){baseSdkUtils[tag_name][relatedProductLine][relatedProductName] = 1.0};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (_rel_mesh_ &gt; 0){theThis.scale.y = _rel_mesh_};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (_rel_mesh_ === 0){theThis.scale.y = 0.0001};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;catch (error) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;})<br />
}<br />
<br />
function DateTime_Attr(data){<br />
&nbsp;&nbsp;&nbsp;&nbsp;if ( baseSdkUtils.isSuccess(data) ){ <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _temp_item_ = data.getElementsByTagName('item')[0];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _attr_ = baseSdkUtils.decode(_temp_item_.getAttribute('datetime'));&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (_attr_.length === 0 || _attr_ == null){return 'error';}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _datetime_type_date_ = new Date(baseSdkUtils.toDateObject(_attr_));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return _datetime_type_date_;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
function Text_Attr(data, attr, num){<br />
&nbsp;&nbsp;&nbsp;&nbsp;if ( baseSdkUtils.isSuccess(data) ){ <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _temp_item_ = data.getElementsByTagName('item')[num];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _attr_ = baseSdkUtils.decode(_temp_item_.getAttribute(attr));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return _attr_;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
baseSdkUtils.spriteTexture = {};<br />
// МОДИФИЦИРОВАНО: добавлен параметр programname<br />
baseSdkUtils.spriteTexture = function(theThis, hexcolor, canvas_rect1, context_rect1, relatedProductLine, relatedProductName, _font_, _text_, color, texture, feedrate, alarmmessage, programname){<br />
&nbsp;&nbsp;&nbsp;&nbsp;hexcolor = '#' + color.getHexString();<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.clearRect(0, 0, canvas_rect1.width, canvas_rect1.height);  <br />
&nbsp;&nbsp;&nbsp;&nbsp;var _contextWidth_ = context_rect1.measureText(_text_).width + 20;<br />
&nbsp;&nbsp;&nbsp;&nbsp;var _contextHeight_ = context_rect1.measureText(_text_).fontBoundingBoxAscent + context_rect1.measureText(_text_).fontBoundingBoxDescent ;<br />
&nbsp;&nbsp;&nbsp;&nbsp;canvas_rect1.width = _contextWidth_ + 40 + 100;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;// МОДИФИЦИРОВАНО: увеличение высоты для размещения имени программы<br />
&nbsp;&nbsp;&nbsp;&nbsp;canvas_rect1.height = _contextHeight_ + 90;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;theThis.scale.x = theThis.scale.y*((canvas_rect1.width)/canvas_rect1.height);<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.beginPath();<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillStyle = "rgba(255, 255, 240, 0.8)";<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillRect(20, 0, _contextWidth_ + 20, _contextHeight_);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillStyle = hexcolor;<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillRect(0, 0, 20, _contextHeight_);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.beginPath();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.stroke();<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.beginPath();<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.strokeStyle = "grey";<br />
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.moveTo(20, _contextHeight_);<br />
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.lineTo(40, _contextHeight_/2);<br />
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.lineTo(20, 0);<br />
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fill();<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.stroke();<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillStyle = 'yellow';<br />
&nbsp;&nbsp;&nbsp;&nbsp;var _tag_arr_ = ['NC_ON', 'NC_PROGRAM_RUN', 'NC_WIP'];<br />
&nbsp;&nbsp;&nbsp;&nbsp;for (var i = 0; i &lt; _tag_arr_.length; i++){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ( base3DUtils[_tag_arr_[i]] === undefined ){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;base3DUtils[_tag_arr_[i]] = {};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ( base3DUtils[_tag_arr_[i]][relatedProductLine] === undefined ){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;base3DUtils[_tag_arr_[i]][relatedProductLine] = {};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;base3DUtils[_tag_arr_[i]][relatedProductLine][relatedProductName] = 0.00;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillRect(0, _contextHeight_ + 10, canvas_rect1.width * base3DUtils['NC_ON'][relatedProductLine][relatedProductName], 20);&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillStyle = 'GreenYellow';<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillRect(0, _contextHeight_ + 10, canvas_rect1.width * base3DUtils['NC_PROGRAM_RUN'][relatedProductLine][relatedProductName], 20);<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillStyle = 'green';<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillRect(0, _contextHeight_ + 10, canvas_rect1.width * base3DUtils['NC_WIP'][relatedProductLine][relatedProductName], 20);&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.textAlign = "center";<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillStyle = "black";<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.font = '18px "bahnschrift semicondensed"';<br />
&nbsp;&nbsp;&nbsp;&nbsp;//var _text_temp_ = 'По программе в смену ';<br />
&nbsp;&nbsp;&nbsp;&nbsp;var _text_temp_ = '';<br />
&nbsp;&nbsp;&nbsp;&nbsp;_text_temp_ = _text_temp_ + (base3DUtils['NC_PROGRAM_RUN'][relatedProductLine][relatedProductName]*100).toFixed(2) + ' %';&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (base3DUtils['NC_PROGRAM_RUN'][relatedProductLine]['hour'] !== undefined &amp;&amp; base3DUtils['NC_PROGRAM_RUN'][relatedProductLine]['hour'][relatedProductName] !== undefined){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_text_temp_ = _text_temp_ + ' (' + base3DUtils['NC_PROGRAM_RUN'][relatedProductLine]['hour'][relatedProductName] + ' ч.)';<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillText(_text_temp_, _contextWidth_/2 + 20, _contextHeight_ + 25);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;// ДОБАВЛЕНО: отображение имени программы внизу спрайта<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (programname !== undefined &amp;&amp; programname !== '') {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillStyle = "blue";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.font = '14px "bahnschrift semicondensed"';<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillText('' + programname, _contextWidth_/2 + 20, _contextHeight_ + 50);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillStyle = "black";&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.font = _font_;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillText(_text_, _contextWidth_/2 + 40, context_rect1.measureText(_text_).fontBoundingBoxAscent);&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;// прямоугольник справа<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillStyle = "rgba(255, 255, 240, 0.8)";<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillRect(_contextWidth_ + 50, 0, _contextWidth_ + 20, _contextHeight_);<br />
&nbsp;&nbsp;&nbsp;&nbsp;// коррекция подачи<br />
&nbsp;&nbsp;&nbsp;&nbsp;//_text_temp_ = ' ' + feedrate + '%';<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (feedrate === undefined)(_text_temp_ = '');<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (feedrate !== undefined) {&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (feedrate != ''){context_rect1.fillStyle = "black";context_rect1.strokeStyle = "green";_text_temp_ = ' ' + feedrate + '%';}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (feedrate == ''){context_rect1.fillStyle = "black";context_rect1.strokeStyle = "red";_text_temp_ = '';}&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.beginPath();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var offset = 50;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var offsetV = 20; var heightF = 8;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.lineWidth = 2;<br />
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.moveTo(_contextWidth_ + offset + 10,  offsetV);<br />
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.lineTo(_contextWidth_ + offset + 12.5, offsetV - heightF);<br />
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.lineTo(_contextWidth_ + offset + 15, offsetV + heightF);&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.lineTo(_contextWidth_ + offset + 17.5, offsetV - heightF);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.lineTo(_contextWidth_ + offset + 20, offsetV + heightF);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.lineTo(_contextWidth_ + offset + 22.5, offsetV - heightF);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.lineTo(_contextWidth_ + offset + 25, offsetV + heightF);&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.lineTo(_contextWidth_ + offset + 27.5, offsetV - heightF);&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.lineTo(_contextWidth_ + offset + 30, offsetV + heightF);&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.lineTo(_contextWidth_ + offset + 32.5, offsetV);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.stroke();&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.font = '24px "bahnschrift semicondensed"';<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillText(_text_temp_, _contextWidth_ + offset + 60, 25);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;// аларм<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (alarmmessage !== undefined) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ( alarmmessage != ''){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillStyle = "red";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else{context_rect1.fillStyle = "grey";}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.font = '24px FontAwesome';<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillText('&#92;uF071', _contextWidth_ + offset + 20, 60);&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillStyle = "black";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.font = '12px "bahnschrift semicondensed"';<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.textAlign = "left";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillText(alarmmessage.substring(0, 10), _contextWidth_ + offset + 40, 60);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillText(alarmmessage.substring(10, 16), _contextWidth_ + offset + 5, 75);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.font = _font_;<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.textAlign = "center";<br />
&nbsp;&nbsp;&nbsp;&nbsp;//<br />
&nbsp;&nbsp;&nbsp;&nbsp;texture.needsUpdate = true;<br />
}<br />
<br />
<br />
baseSdkUtils.getLastTagCalculation = {};<br />
baseSdkUtils.getLastTagCalculation = function(appid, pid, tid, endTime, timeout_mills, callBackFunc){<br />
&nbsp;&nbsp;&nbsp;&nbsp;var _attr_ = '';<br />
&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils.service.WNApplicationTagHelper.getLastTagCalculationValue(appid, pid, tid, curDateTimeText, timeout_mills, function(data){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ( baseSdkUtils.isSuccess(data) ){ <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(debug){console.log('getLastTagCalculation for ' + pid + ' :'); console.log(data)};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(data.getElementsByTagName('item').length === 0){return;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _temp_item_ = data.getElementsByTagName('item')[0];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_attr_ = baseSdkUtils.decode(_temp_item_.getAttribute('value'));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ( callBackFunc ){<br />
&nbsp;&nbsp;&nbsp;&nbsp;    &nbsp;&nbsp;&nbsp;&nbsp;callBackFunc.call( this, _attr_);<br />
&nbsp;&nbsp;&nbsp;&nbsp;    }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;})<br />
}</code></div></div><br />
<br />
<br />
Теперь вместе с колбами у нас есть данные для отображения. <br />
<br />
В нашем случае мы можем немного изменить табличку с именем станка, и добавить полученное значение:<br />
<br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>var relatedProductName = '20614';// ввести имя детали станка, к которой привязан сигнал<br />
var relatedProductLine = '1';<br />
var _font_ = '30px "bahnschrift semicondensed"'; // шрифт по умолчаию<br />
var _text_ = 'Не найдено'; // имя по умолчанию<br />
var flag_text = 1; // 0 - отображать имя по умолчанию из переменной _text_. 1 - отображать фактическое наименование станка<br />
var color = new THREE.Color( 'black' );<br />
var obj=scene.getObjectByName(relatedProductName); <br />
var _poid_ = '';<br />
if (obj !== undefined){<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (obj.material !== undefined &amp;&amp; obj.material.color !== undefined){color=obj.material.color;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (&nbsp;&nbsp;&nbsp;&nbsp;base3DUtils.WNSignalArray[ obj.uuid ] !== undefined &amp;&amp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;base3DUtils.WNSignalArray[ obj.uuid ].length != undefined &amp;&amp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;base3DUtils.WNSignalArray[ obj.uuid ].length &gt; 0 &amp;&amp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;flag_text == 1){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp;_text_ = base3DUtils.WNSignalArray[ obj.uuid ][0].product_name;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_text_ = base3DUtils.WNSignalArray[ obj.uuid ][0].product_serial;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp;_text_ = _text_.split(' ')[0];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_poid_ = base3DUtils.WNSignalArray[ obj.uuid ][0].product_oid;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
}<br />
//<br />
var hexcolor = '#' + color.getHexString();<br />
var delay = 0; <br />
var delay_tag = 0; <br />
var delay_start_tag = 1000; <br />
var feedrate_override = '';<br />
var alarmmessage = ''<br />
// ДОБАВЛЕНО: переменная для имени программы<br />
var program_name = '';<br />
var canvas_rect1 = document.createElement('canvas');<br />
var context_rect1 = canvas_rect1.getContext('2d');<br />
context_rect1.fillStyle = 'white';<br />
context_rect1.beginPath();<br />
context_rect1.stroke();<br />
context_rect1.font = _font_;<br />
 var texture = new THREE.Texture(canvas_rect1)<br />
 texture.anisotropy = renderer.getMaxAnisotropy();<br />
 texture.needsUpdate = true;<br />
 var material = new THREE.SpriteMaterial({ map: texture, color: 'white' });<br />
 material.map.minFilter = THREE.LinearFilter;<br />
this.material.map = texture;<br />
//this.material.transparent = true;<br />
this.material = material;<br />
texture.needsUpdate = true;<br />
function start(event){<br />
&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils.spriteTexture(this, hexcolor, canvas_rect1, context_rect1, relatedProductLine, relatedProductName, _font_, _text_, color, texture);<br />
}<br />
function update (event){<br />
//&nbsp;&nbsp;&nbsp;&nbsp;return;<br />
&nbsp;&nbsp;&nbsp;&nbsp;if ((delay+=event.delta) &gt; 500){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (obj !== undefined &amp;&amp; obj.material !== undefined &amp;&amp; obj.material.color !== undefined){color = obj.material.color;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp;if (hexcolor == '#' + color.getHexString()){return;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// МОДИФИЦИРОВАНО: добавлен параметр program_name<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils.spriteTexture(this, hexcolor, canvas_rect1, context_rect1, relatedProductLine, relatedProductName, _font_, _text_, color, texture, feedrate_override, alarmmessage, program_name);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delay = 0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;if ((delay_tag+=event.delta) &gt; delay_start_tag){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delay_start_tag = scene.userData.tag_inc;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils.getLastTagCalculation(scene.userData.appid , _poid_, scene.userData.tag_oid_overfeedrate, '', 60000, function(data){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;feedrate_override = data;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils.getLastTagCalculation(scene.userData.appid , _poid_, scene.userData.tag_oid_alarmmessage, '', 60000, function(data){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;alarmmessage = data;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// ДОБАВЛЕНО: получение имени программы<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils.getLastTagCalculation(scene.userData.appid , _poid_, scene.userData.tag_oid_programname, '', 60000, function(data){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;program_name = data;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delay_tag = 0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;<br />
}</code></div></div><br />
<br />
Вы можете попробовать свой вариант вывода других сигналов, привязав к ним свой тег и добавив его для отображения.]]></description>
			<content:encoded><![CDATA[Добрый день!<br />
<br />
Если вы хотите добавить в 3Д сцену отображение сигнала, то лучший способ сделать это - через тег.<br />
<br />
Рассмотрим на примере модификации таблички с именем станка для отображения текущего имени запущенной программы.<br />
<br />
Для начала добавим тег к учету, поскольку это уже делается в объекте для работы колб и тех же самых табличек - мы легко сможем улучшить его.<br />
<br />
<br />
WNScriptForCylinders<br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>var currentDate = new Date();<br />
var curDateTimeText = '';<br />
var delay = 0;<br />
var delay_limit = 10000;<br />
var debug = false;<br />
scene.userData.appid = 'winnum.org.app.WNApplicationInstance:1';<br />
var tag_oid;<br />
_getPlatformDateTime_();<br />
function update( event ) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;delay += parseInt(event.delta); <br />
&nbsp;&nbsp;&nbsp;&nbsp;if ( delay &lt; delay_limit ) { return; } else { delay = 0; } //delay for delay_limit<br />
&nbsp;&nbsp;&nbsp;&nbsp;_getPlatformDateTime_();&nbsp;&nbsp;&nbsp;&nbsp;<br />
}<br />
<br />
// Время платформы<br />
function _getPlatformDateTime_(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils.service.WNCalendarHelper.getPlatformDateTime(function(data){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var curDateTime = DateTime_Attr(data);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;currentDate = new Date(curDateTime);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (debug) {console.log('current DateTime Platform:');console.log(curDateTime)};<br />
&nbsp;&nbsp;&nbsp;&nbsp;});<br />
}<br />
<br />
baseSdkUtils._getTag_ = {};<br />
baseSdkUtils._getTag_ = function(tag_name){<br />
&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils.service.WNApplicationHelper.getTag(scene.userData.appid, function(data){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ( baseSdkUtils.isSuccess(data) ){ <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(var i = 0; i &lt; data.getElementsByTagName('item').length ; i++ ) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _temp_item_ = data.getElementsByTagName('item')[i];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _attr_ = baseSdkUtils.decode(_temp_item_.getAttribute('tag'));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (_attr_ == tag_name){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(tag_name == 'NC_ON'){scene.userData.tag_oid1 =  baseSdkUtils.decode(_temp_item_.getAttribute('id'));}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(tag_name == 'NC_PROGRAM_RUN'){scene.userData.tag_oid2 =  baseSdkUtils.decode(_temp_item_.getAttribute('id'));}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(tag_name == 'NC_WIP'){scene.userData.tag_oid3 =  baseSdkUtils.decode(_temp_item_.getAttribute('id'));}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(tag_name == 'NC_FEEDRATE_OVERRIDE'){scene.userData.tag_oid_overfeedrate =  baseSdkUtils.decode(_temp_item_.getAttribute('id'));}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(tag_name == 'NC_ALARM_MESSAGE'){scene.userData.tag_oid_alarmmessage =  baseSdkUtils.decode(_temp_item_.getAttribute('id'));}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// ДОБАВЛЕНО: поиск тега NC_PROGRAM_NAME<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(tag_name == 'NC_PROGRAM_NAME'){scene.userData.tag_oid_programname =  baseSdkUtils.decode(_temp_item_.getAttribute('id'));}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;scene.userData.tag_oid =  baseSdkUtils.decode(_temp_item_.getAttribute('id'));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (debug){console.log('tag_oid for ' + tag_name + ':');console.log(scene.userData.tag_oid)};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;})<br />
}<br />
<br />
function start(event){<br />
&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils._getTag_('NC_ON');<br />
&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils._getTag_('NC_PROGRAM_RUN');<br />
&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils._getTag_('NC_WIP');<br />
&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils._getTag_('NC_FEEDRATE_OVERRIDE');<br />
&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils._getTag_('NC_ALARM_MESSAGE');&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;// ДОБАВЛЕНО: инициализация поиска тега NC_PROGRAM_NAME<br />
&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils._getTag_('NC_PROGRAM_NAME');&nbsp;&nbsp;&nbsp;&nbsp;<br />
}<br />
<br />
baseSdkUtils._getCurrentWorkShiftStartDate_ = {};<br />
baseSdkUtils._getCurrentWorkShiftStartDate_ = function(_poid_, theThis, tag_name, relatedProductLine, relatedProductName){<br />
&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils.service.WNCalendarHelper.getCurrentWorkShiftStartDate(_poid_, function(data){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var startShiftDate = DateTime_Attr(data);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (debug){console.log('start current shift Date:'); console.log(startShiftDate)};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (startShiftDate == 'error'){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(debug){console.log('Нет текущей смены для ' + _poid_)};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;base3DUtils[tag_name][relatedProductLine][relatedProductName] = 0.0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;theThis.scale.y = 0.0001;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var startTime = baseSdkUtils.toDateTimeString(startShiftDate, true);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var endTime = baseSdkUtils.toDateTimeString(currentDate, true);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _tag_oid_ = '';<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(tag_name == 'NC_ON'){_tag_oid_ = scene.userData.tag_oid1 ;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(tag_name == 'NC_PROGRAM_RUN'){_tag_oid_ = scene.userData.tag_oid2 ;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(tag_name == 'NC_WIP'){_tag_oid_ = scene.userData.tag_oid3 ;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils.service.WNApplicationTagHelper.getSimpleTagCalculation(scene.userData.appid, _poid_, _tag_oid_, startTime, endTime, 60000, function(TagCalculation_data){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ( baseSdkUtils.isSuccess(TagCalculation_data) ){ <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(debug){console.log('getSimpleTagCalculation for ' + _poid_ + ' :'); console.log(TagCalculation_data)};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(TagCalculation_data.getElementsByTagName('item').length === 0){return;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(var i = 0; i &lt; TagCalculation_data.getElementsByTagName('item').length ; i++ ) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _temp_item_ = TagCalculation_data.getElementsByTagName('item')[i];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _attr_ = baseSdkUtils.decode(_temp_item_.getAttribute('percent'));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _attr1_ = parseFloat(baseSdkUtils.decode(_temp_item_.getAttribute('hours'))).toFixed(2);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _rel_mesh_ = _attr_/100;<br />
                               &nbsp;&nbsp;if (base3DUtils[tag_name][relatedProductLine]['hour'] === undefined){<br />
                                          base3DUtils[tag_name][relatedProductLine]['hour'] = {};<br />
                                          base3DUtils[tag_name][relatedProductLine]['hour'][relatedProductName] = 0.0;<br />
                                  }              <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;base3DUtils[tag_name][relatedProductLine][relatedProductName] =_rel_mesh_;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;base3DUtils[tag_name][relatedProductLine]['hour'][relatedProductName] =_attr1_;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (_rel_mesh_ &gt; 1){baseSdkUtils[tag_name][relatedProductLine][relatedProductName] = 1.0};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (_rel_mesh_ &gt; 0){theThis.scale.y = _rel_mesh_};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (_rel_mesh_ === 0){theThis.scale.y = 0.0001};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;catch (error) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;})<br />
}<br />
<br />
function DateTime_Attr(data){<br />
&nbsp;&nbsp;&nbsp;&nbsp;if ( baseSdkUtils.isSuccess(data) ){ <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _temp_item_ = data.getElementsByTagName('item')[0];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _attr_ = baseSdkUtils.decode(_temp_item_.getAttribute('datetime'));&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (_attr_.length === 0 || _attr_ == null){return 'error';}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _datetime_type_date_ = new Date(baseSdkUtils.toDateObject(_attr_));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return _datetime_type_date_;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
function Text_Attr(data, attr, num){<br />
&nbsp;&nbsp;&nbsp;&nbsp;if ( baseSdkUtils.isSuccess(data) ){ <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _temp_item_ = data.getElementsByTagName('item')[num];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _attr_ = baseSdkUtils.decode(_temp_item_.getAttribute(attr));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return _attr_;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
baseSdkUtils.spriteTexture = {};<br />
// МОДИФИЦИРОВАНО: добавлен параметр programname<br />
baseSdkUtils.spriteTexture = function(theThis, hexcolor, canvas_rect1, context_rect1, relatedProductLine, relatedProductName, _font_, _text_, color, texture, feedrate, alarmmessage, programname){<br />
&nbsp;&nbsp;&nbsp;&nbsp;hexcolor = '#' + color.getHexString();<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.clearRect(0, 0, canvas_rect1.width, canvas_rect1.height);  <br />
&nbsp;&nbsp;&nbsp;&nbsp;var _contextWidth_ = context_rect1.measureText(_text_).width + 20;<br />
&nbsp;&nbsp;&nbsp;&nbsp;var _contextHeight_ = context_rect1.measureText(_text_).fontBoundingBoxAscent + context_rect1.measureText(_text_).fontBoundingBoxDescent ;<br />
&nbsp;&nbsp;&nbsp;&nbsp;canvas_rect1.width = _contextWidth_ + 40 + 100;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;// МОДИФИЦИРОВАНО: увеличение высоты для размещения имени программы<br />
&nbsp;&nbsp;&nbsp;&nbsp;canvas_rect1.height = _contextHeight_ + 90;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;theThis.scale.x = theThis.scale.y*((canvas_rect1.width)/canvas_rect1.height);<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.beginPath();<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillStyle = "rgba(255, 255, 240, 0.8)";<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillRect(20, 0, _contextWidth_ + 20, _contextHeight_);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillStyle = hexcolor;<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillRect(0, 0, 20, _contextHeight_);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.beginPath();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.stroke();<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.beginPath();<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.strokeStyle = "grey";<br />
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.moveTo(20, _contextHeight_);<br />
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.lineTo(40, _contextHeight_/2);<br />
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.lineTo(20, 0);<br />
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fill();<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.stroke();<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillStyle = 'yellow';<br />
&nbsp;&nbsp;&nbsp;&nbsp;var _tag_arr_ = ['NC_ON', 'NC_PROGRAM_RUN', 'NC_WIP'];<br />
&nbsp;&nbsp;&nbsp;&nbsp;for (var i = 0; i &lt; _tag_arr_.length; i++){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ( base3DUtils[_tag_arr_[i]] === undefined ){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;base3DUtils[_tag_arr_[i]] = {};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ( base3DUtils[_tag_arr_[i]][relatedProductLine] === undefined ){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;base3DUtils[_tag_arr_[i]][relatedProductLine] = {};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;base3DUtils[_tag_arr_[i]][relatedProductLine][relatedProductName] = 0.00;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillRect(0, _contextHeight_ + 10, canvas_rect1.width * base3DUtils['NC_ON'][relatedProductLine][relatedProductName], 20);&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillStyle = 'GreenYellow';<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillRect(0, _contextHeight_ + 10, canvas_rect1.width * base3DUtils['NC_PROGRAM_RUN'][relatedProductLine][relatedProductName], 20);<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillStyle = 'green';<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillRect(0, _contextHeight_ + 10, canvas_rect1.width * base3DUtils['NC_WIP'][relatedProductLine][relatedProductName], 20);&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.textAlign = "center";<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillStyle = "black";<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.font = '18px "bahnschrift semicondensed"';<br />
&nbsp;&nbsp;&nbsp;&nbsp;//var _text_temp_ = 'По программе в смену ';<br />
&nbsp;&nbsp;&nbsp;&nbsp;var _text_temp_ = '';<br />
&nbsp;&nbsp;&nbsp;&nbsp;_text_temp_ = _text_temp_ + (base3DUtils['NC_PROGRAM_RUN'][relatedProductLine][relatedProductName]*100).toFixed(2) + ' %';&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (base3DUtils['NC_PROGRAM_RUN'][relatedProductLine]['hour'] !== undefined &amp;&amp; base3DUtils['NC_PROGRAM_RUN'][relatedProductLine]['hour'][relatedProductName] !== undefined){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_text_temp_ = _text_temp_ + ' (' + base3DUtils['NC_PROGRAM_RUN'][relatedProductLine]['hour'][relatedProductName] + ' ч.)';<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillText(_text_temp_, _contextWidth_/2 + 20, _contextHeight_ + 25);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;// ДОБАВЛЕНО: отображение имени программы внизу спрайта<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (programname !== undefined &amp;&amp; programname !== '') {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillStyle = "blue";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.font = '14px "bahnschrift semicondensed"';<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillText('' + programname, _contextWidth_/2 + 20, _contextHeight_ + 50);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillStyle = "black";&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.font = _font_;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillText(_text_, _contextWidth_/2 + 40, context_rect1.measureText(_text_).fontBoundingBoxAscent);&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;// прямоугольник справа<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillStyle = "rgba(255, 255, 240, 0.8)";<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillRect(_contextWidth_ + 50, 0, _contextWidth_ + 20, _contextHeight_);<br />
&nbsp;&nbsp;&nbsp;&nbsp;// коррекция подачи<br />
&nbsp;&nbsp;&nbsp;&nbsp;//_text_temp_ = ' ' + feedrate + '%';<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (feedrate === undefined)(_text_temp_ = '');<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (feedrate !== undefined) {&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (feedrate != ''){context_rect1.fillStyle = "black";context_rect1.strokeStyle = "green";_text_temp_ = ' ' + feedrate + '%';}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (feedrate == ''){context_rect1.fillStyle = "black";context_rect1.strokeStyle = "red";_text_temp_ = '';}&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.beginPath();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var offset = 50;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var offsetV = 20; var heightF = 8;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.lineWidth = 2;<br />
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.moveTo(_contextWidth_ + offset + 10,  offsetV);<br />
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.lineTo(_contextWidth_ + offset + 12.5, offsetV - heightF);<br />
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.lineTo(_contextWidth_ + offset + 15, offsetV + heightF);&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.lineTo(_contextWidth_ + offset + 17.5, offsetV - heightF);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.lineTo(_contextWidth_ + offset + 20, offsetV + heightF);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.lineTo(_contextWidth_ + offset + 22.5, offsetV - heightF);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.lineTo(_contextWidth_ + offset + 25, offsetV + heightF);&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.lineTo(_contextWidth_ + offset + 27.5, offsetV - heightF);&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.lineTo(_contextWidth_ + offset + 30, offsetV + heightF);&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.lineTo(_contextWidth_ + offset + 32.5, offsetV);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.stroke();&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.font = '24px "bahnschrift semicondensed"';<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillText(_text_temp_, _contextWidth_ + offset + 60, 25);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;// аларм<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (alarmmessage !== undefined) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ( alarmmessage != ''){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillStyle = "red";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else{context_rect1.fillStyle = "grey";}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.font = '24px FontAwesome';<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillText('&#92;uF071', _contextWidth_ + offset + 20, 60);&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillStyle = "black";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.font = '12px "bahnschrift semicondensed"';<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.textAlign = "left";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillText(alarmmessage.substring(0, 10), _contextWidth_ + offset + 40, 60);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.fillText(alarmmessage.substring(10, 16), _contextWidth_ + offset + 5, 75);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.font = _font_;<br />
&nbsp;&nbsp;&nbsp;&nbsp;context_rect1.textAlign = "center";<br />
&nbsp;&nbsp;&nbsp;&nbsp;//<br />
&nbsp;&nbsp;&nbsp;&nbsp;texture.needsUpdate = true;<br />
}<br />
<br />
<br />
baseSdkUtils.getLastTagCalculation = {};<br />
baseSdkUtils.getLastTagCalculation = function(appid, pid, tid, endTime, timeout_mills, callBackFunc){<br />
&nbsp;&nbsp;&nbsp;&nbsp;var _attr_ = '';<br />
&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils.service.WNApplicationTagHelper.getLastTagCalculationValue(appid, pid, tid, curDateTimeText, timeout_mills, function(data){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ( baseSdkUtils.isSuccess(data) ){ <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(debug){console.log('getLastTagCalculation for ' + pid + ' :'); console.log(data)};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(data.getElementsByTagName('item').length === 0){return;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var _temp_item_ = data.getElementsByTagName('item')[0];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_attr_ = baseSdkUtils.decode(_temp_item_.getAttribute('value'));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ( callBackFunc ){<br />
&nbsp;&nbsp;&nbsp;&nbsp;    &nbsp;&nbsp;&nbsp;&nbsp;callBackFunc.call( this, _attr_);<br />
&nbsp;&nbsp;&nbsp;&nbsp;    }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;})<br />
}</code></div></div><br />
<br />
<br />
Теперь вместе с колбами у нас есть данные для отображения. <br />
<br />
В нашем случае мы можем немного изменить табличку с именем станка, и добавить полученное значение:<br />
<br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>var relatedProductName = '20614';// ввести имя детали станка, к которой привязан сигнал<br />
var relatedProductLine = '1';<br />
var _font_ = '30px "bahnschrift semicondensed"'; // шрифт по умолчаию<br />
var _text_ = 'Не найдено'; // имя по умолчанию<br />
var flag_text = 1; // 0 - отображать имя по умолчанию из переменной _text_. 1 - отображать фактическое наименование станка<br />
var color = new THREE.Color( 'black' );<br />
var obj=scene.getObjectByName(relatedProductName); <br />
var _poid_ = '';<br />
if (obj !== undefined){<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (obj.material !== undefined &amp;&amp; obj.material.color !== undefined){color=obj.material.color;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (&nbsp;&nbsp;&nbsp;&nbsp;base3DUtils.WNSignalArray[ obj.uuid ] !== undefined &amp;&amp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;base3DUtils.WNSignalArray[ obj.uuid ].length != undefined &amp;&amp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;base3DUtils.WNSignalArray[ obj.uuid ].length &gt; 0 &amp;&amp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;flag_text == 1){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp;_text_ = base3DUtils.WNSignalArray[ obj.uuid ][0].product_name;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_text_ = base3DUtils.WNSignalArray[ obj.uuid ][0].product_serial;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp;_text_ = _text_.split(' ')[0];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_poid_ = base3DUtils.WNSignalArray[ obj.uuid ][0].product_oid;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
}<br />
//<br />
var hexcolor = '#' + color.getHexString();<br />
var delay = 0; <br />
var delay_tag = 0; <br />
var delay_start_tag = 1000; <br />
var feedrate_override = '';<br />
var alarmmessage = ''<br />
// ДОБАВЛЕНО: переменная для имени программы<br />
var program_name = '';<br />
var canvas_rect1 = document.createElement('canvas');<br />
var context_rect1 = canvas_rect1.getContext('2d');<br />
context_rect1.fillStyle = 'white';<br />
context_rect1.beginPath();<br />
context_rect1.stroke();<br />
context_rect1.font = _font_;<br />
 var texture = new THREE.Texture(canvas_rect1)<br />
 texture.anisotropy = renderer.getMaxAnisotropy();<br />
 texture.needsUpdate = true;<br />
 var material = new THREE.SpriteMaterial({ map: texture, color: 'white' });<br />
 material.map.minFilter = THREE.LinearFilter;<br />
this.material.map = texture;<br />
//this.material.transparent = true;<br />
this.material = material;<br />
texture.needsUpdate = true;<br />
function start(event){<br />
&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils.spriteTexture(this, hexcolor, canvas_rect1, context_rect1, relatedProductLine, relatedProductName, _font_, _text_, color, texture);<br />
}<br />
function update (event){<br />
//&nbsp;&nbsp;&nbsp;&nbsp;return;<br />
&nbsp;&nbsp;&nbsp;&nbsp;if ((delay+=event.delta) &gt; 500){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (obj !== undefined &amp;&amp; obj.material !== undefined &amp;&amp; obj.material.color !== undefined){color = obj.material.color;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp;if (hexcolor == '#' + color.getHexString()){return;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// МОДИФИЦИРОВАНО: добавлен параметр program_name<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils.spriteTexture(this, hexcolor, canvas_rect1, context_rect1, relatedProductLine, relatedProductName, _font_, _text_, color, texture, feedrate_override, alarmmessage, program_name);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delay = 0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;if ((delay_tag+=event.delta) &gt; delay_start_tag){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delay_start_tag = scene.userData.tag_inc;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils.getLastTagCalculation(scene.userData.appid , _poid_, scene.userData.tag_oid_overfeedrate, '', 60000, function(data){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;feedrate_override = data;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils.getLastTagCalculation(scene.userData.appid , _poid_, scene.userData.tag_oid_alarmmessage, '', 60000, function(data){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;alarmmessage = data;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// ДОБАВЛЕНО: получение имени программы<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;baseSdkUtils.getLastTagCalculation(scene.userData.appid , _poid_, scene.userData.tag_oid_programname, '', 60000, function(data){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;program_name = data;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delay_tag = 0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;<br />
}</code></div></div><br />
<br />
Вы можете попробовать свой вариант вывода других сигналов, привязав к ним свой тег и добавив его для отображения.]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Получение параметров из адресной строки]]></title>
			<link>https://community.winnum.io/showthread.php?tid=58</link>
			<pubDate>Tue, 21 Apr 2026 14:53:19 +0300</pubDate>
			<dc:creator><![CDATA[<a href="https://community.winnum.io/member.php?action=profile&uid=8">Lamantur</a>]]></dc:creator>
			<guid isPermaLink="false">https://community.winnum.io/showthread.php?tid=58</guid>
			<description><![CDATA[Добрый день!<br />
<br />
Когда вы создаете рассылку в меню вам предлагают указать параметры url:<br />
<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://community.winnum.io/images/attachtypes/image.png" title="PNG Image" border="0" alt=".png" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=61" target="_blank" title="">создание рассылки.png</a> (Размер: 43.08 KB / Загрузок: 7)
<!-- end: postbit_attachments_attachment --><br />
<br />
В случае с приложением "Бюллетени станков" - у нас есть инструкция, как поступать с этим параметром, но что если мы создали динамическое приложение и хотим использовать его как рассылку? Мы прочитали значение функций, отвечающих за публикацию и разместили ее в нужном месте кода, рассылка работает, но поле "Параметры url" остаются не востребованными.<br />
<br />
Сегодня я расскажу не только о том как использовать это поле, но и возможности передачи небольшого сообщения прямо в адресной строке.<br />
<br />
Предположим у моего нового приложения такой адрес:<br />
'http://127.0.0.1/Winnum/views/pages/app/ui/designer/list.jsp?oid=winnum.org.app.WNApplication:61&amp;mode=yes'<br />
<br />
Из этого адреса понятно, что основная его часть отвечает за открытие экземпляра приложения, oid - это класс самого приложения, и еще один параметр mode.<br />
<br />
Пользуясь этими знаниями мы можем задать любой параметр прямо в адресной строке, чем и воспользуемся:<br />
'http://127.0.0.1/Winnum/views/pages/app/ui/designer/list.jsp?oid=winnum.org.app.WNApplication:61&amp;mode=yes&amp;product=winnum.org.product.WNProduct:3'<br />
Я просто добавил новый параметр product прямо в адресную строку, если загрузить страницу так, то ничего не поменяется. В коде страницы просто нет обработки этого параметра, поэтому он игнорируется<br />
<br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>    const urlParams = new URLSearchParams(window.location.search);<br />
    const product = urlParams.get('product');</code></div></div><br />
теперь переменная product содержит переданный класс продукта, а значит:<br />
<br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>getProduct(product, async (data)=&gt;{<br />
        console.log(data);<br />
        equipmentData = data;<br />
        renderEquipmentTable(data);<br />
...</code></div></div><br />
т.е. мы спокойно можем пользоваться этим параметром. Таким образом можно передавать любые параметры, задавать их динамически при переходе по ссылке, например на другую страницу, там тоже можно указывать параметры.<br />
<a href="https://community.winnum.io/showthread.php?tid=34" target="_blank" rel="noopener" class="mycode_url">Как сделать переход на страницу с параметрами</a><br />
<br />
А при создании рассылки все параметры url указываются в отдельном поле так же как они записываются в адресе, через &amp;.<br />
<br />
Спасибо за внимание!]]></description>
			<content:encoded><![CDATA[Добрый день!<br />
<br />
Когда вы создаете рассылку в меню вам предлагают указать параметры url:<br />
<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://community.winnum.io/images/attachtypes/image.png" title="PNG Image" border="0" alt=".png" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=61" target="_blank" title="">создание рассылки.png</a> (Размер: 43.08 KB / Загрузок: 7)
<!-- end: postbit_attachments_attachment --><br />
<br />
В случае с приложением "Бюллетени станков" - у нас есть инструкция, как поступать с этим параметром, но что если мы создали динамическое приложение и хотим использовать его как рассылку? Мы прочитали значение функций, отвечающих за публикацию и разместили ее в нужном месте кода, рассылка работает, но поле "Параметры url" остаются не востребованными.<br />
<br />
Сегодня я расскажу не только о том как использовать это поле, но и возможности передачи небольшого сообщения прямо в адресной строке.<br />
<br />
Предположим у моего нового приложения такой адрес:<br />
'http://127.0.0.1/Winnum/views/pages/app/ui/designer/list.jsp?oid=winnum.org.app.WNApplication:61&amp;mode=yes'<br />
<br />
Из этого адреса понятно, что основная его часть отвечает за открытие экземпляра приложения, oid - это класс самого приложения, и еще один параметр mode.<br />
<br />
Пользуясь этими знаниями мы можем задать любой параметр прямо в адресной строке, чем и воспользуемся:<br />
'http://127.0.0.1/Winnum/views/pages/app/ui/designer/list.jsp?oid=winnum.org.app.WNApplication:61&amp;mode=yes&amp;product=winnum.org.product.WNProduct:3'<br />
Я просто добавил новый параметр product прямо в адресную строку, если загрузить страницу так, то ничего не поменяется. В коде страницы просто нет обработки этого параметра, поэтому он игнорируется<br />
<br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>    const urlParams = new URLSearchParams(window.location.search);<br />
    const product = urlParams.get('product');</code></div></div><br />
теперь переменная product содержит переданный класс продукта, а значит:<br />
<br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>getProduct(product, async (data)=&gt;{<br />
        console.log(data);<br />
        equipmentData = data;<br />
        renderEquipmentTable(data);<br />
...</code></div></div><br />
т.е. мы спокойно можем пользоваться этим параметром. Таким образом можно передавать любые параметры, задавать их динамически при переходе по ссылке, например на другую страницу, там тоже можно указывать параметры.<br />
<a href="https://community.winnum.io/showthread.php?tid=34" target="_blank" rel="noopener" class="mycode_url">Как сделать переход на страницу с параметрами</a><br />
<br />
А при создании рассылки все параметры url указываются в отдельном поле так же как они записываются в адресе, через &amp;.<br />
<br />
Спасибо за внимание!]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Как получить документацию по Winnum sdk]]></title>
			<link>https://community.winnum.io/showthread.php?tid=47</link>
			<pubDate>Wed, 24 Sep 2025 11:52:05 +0300</pubDate>
			<dc:creator><![CDATA[<a href="https://community.winnum.io/member.php?action=profile&uid=105">artiist172</a>]]></dc:creator>
			<guid isPermaLink="false">https://community.winnum.io/showthread.php?tid=47</guid>
			<description><![CDATA[<div style="text-align: justify;" class="mycode_align">Здравствуйте!  Недавно начал работать с продуктом Winnum и столкнулся с трудностью: не могу найти документацию к Winnum SDK. На официальном сайте разработчика мне найти ее не удалось.</div>
<div style="text-align: justify;" class="mycode_align">Может быть, кто-то из сообщества сталкивался с подобной задачей и знает, где можно скачать руководство по его использованию? Буду очень благодарен за любую помощь или наводку.</div>]]></description>
			<content:encoded><![CDATA[<div style="text-align: justify;" class="mycode_align">Здравствуйте!  Недавно начал работать с продуктом Winnum и столкнулся с трудностью: не могу найти документацию к Winnum SDK. На официальном сайте разработчика мне найти ее не удалось.</div>
<div style="text-align: justify;" class="mycode_align">Может быть, кто-то из сообщества сталкивался с подобной задачей и знает, где можно скачать руководство по его использованию? Буду очень благодарен за любую помощь или наводку.</div>]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Панель с кнопками для 3D v2]]></title>
			<link>https://community.winnum.io/showthread.php?tid=46</link>
			<pubDate>Fri, 19 Sep 2025 12:32:31 +0300</pubDate>
			<dc:creator><![CDATA[<a href="https://community.winnum.io/member.php?action=profile&uid=95">nponyatov</a>]]></dc:creator>
			<guid isPermaLink="false">https://community.winnum.io/showthread.php?tid=46</guid>
			<description><![CDATA[<span style="font-family: Montserrat, arial;" class="mycode_font">В приложении Цифровой двойник довольно часто используются кнопки. Я решил создать вторую версию панели с кнопками для 3D в стиле ЧПУ-станка. Здесь будет представлен расширенный функционал.</span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">В данном посте я хотел бы рассказать о том как добавить их себе в 3D-сцену и настроить. Они смогут помочь стилизовать сцену и улучшить восприятие.</span><br />
<br />
<span style="font-family: Montserrat, arial;" class="mycode_font">1. Скачать файл ViewButtons (во вложении) и импортировать себе в сцену (Файл - Импорт).</span><br />
<img src="https://imglink.io/i/5db0a2ab-2729-4c02-b9da-12129cf5eba4.png" loading="lazy"  alt="[Изображение: 5db0a2ab-2729-4c02-b9da-12129cf5eba4.png]" class="mycode_img" /><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">2. Если мы начнём воспроизведение сцены они появятся в правом нижнем углу. Здесь можно увидеть название цеха, индикатор и 9 кнопок: 4 кнопки с видами, Автооблёт, Ночной режим, Скриншот, Сброс кнопка ИНФО. У каждой кнопки есть hotkey и подписан в скобках.</span><br />
<br />
<span style="font-family: Montserrat, arial;" class="mycode_font">Каждая кнопка с видом при нажатии будет перемещать камеру в заданное место. </span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">При Автооблёте камера будет крутиться вокруг сцены.</span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">Ночной режим выключает освещение на сцене и изменяет её в тёмно-синие оттенки.</span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">Скриншот делает снимок экрана, при этом панель с кнопками не будет на нём отображена.</span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">Сброс отменяет действия и/или возвращает камеру на начальную позицию.</span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">Также снизу есть показатель FPS, текущее время и дата.</span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font"><img src="https://imglink.io/i/735e2d8f-bdfe-4376-9bf3-4b924b7a9ecf.png" loading="lazy"  alt="[Изображение: 735e2d8f-bdfe-4376-9bf3-4b924b7a9ecf.png]" class="mycode_img" /></span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">3. Кнопка ИНФО при нажатии откроет всплывающее окно с системной информацией и технической поддержкой. При нажатии на кнопку support@winnum.ru пользователя будет переносить в почту и начинать письмо в support.</span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font"><img src="https://imglink.io/i/4b433863-0d38-474c-8b07-124fee8d7174.png" loading="lazy"  alt="[Изображение: 4b433863-0d38-474c-8b07-124fee8d7174.png]" class="mycode_img" /></span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">4. Внутри ViewButtons в Объект - Действия будет лежать код самой панели.</span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font"><img src="https://imglink.io/i/169d5b16-6e24-4ce7-8048-f329c056a8bc.png" loading="lazy"  alt="[Изображение: 169d5b16-6e24-4ce7-8048-f329c056a8bc.png]" class="mycode_img" /></span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">5. Чтобы настроить кнопки с видами нужно изменить логику для них в коде. (9-47 строки)</span><br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>function ScriptSView() {<br />
    console.log('Общий вид активирован');<br />
    camera.position.set(12.51, 77.63, 66.77);<br />
    camera.rotation.set(-52 * Math.PI / 180, 0, 0);<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
    // Убираем вызов updateCoordinates()<br />
    updateStatus('ОБЩИЙ ВИД', '#00ff00');<br />
}<br />
<br />
function ScriptView1() {<br />
    console.log('Вид 1 активирован');<br />
    camera.position.set(-54.87, 31.12, 4.72);<br />
    camera.rotation.set(-44 * Math.PI / 180, 0, 0);<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
    // Убираем вызов updateCoordinates()<br />
    updateStatus('ВИД 1', '#00ff00');<br />
}<br />
<br />
function ScriptView2() {<br />
    console.log('Вид 2 активирован');<br />
    camera.position.set(-23.77, 24.68, -17.76);<br />
    camera.rotation.set(-141 * Math.PI / 180, -37.89 * Math.PI / 180, -153.5 * Math.PI / 180);<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
    // Убираем вызов updateCoordinates()<br />
    updateStatus('ВИД 2', '#00ff00');<br />
}<br />
<br />
function ScriptView3() {<br />
    console.log('Вид 3 активирован');<br />
    camera.position.set(30.25, 52.22, 44.24);<br />
    camera.rotation.set(-58.2 * Math.PI / 180, 0, 0);<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
    // Убираем вызов updateCoordinates()<br />
    updateStatus('ВИД 3', '#00ff00');<br />
}</code></div></div><span style="font-family: Montserrat, arial;" class="mycode_font">6. Чтобы изменить названия кнопок видов поменять этот код. (158-164 строки) </span><br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>    // Создаем кнопки видов<br />
    var viewButtons = [<br />
        { id: 'btnSView', text: 'ОБЩИЙ ВИД [1]', onClick: ScriptSView },<br />
        { id: 'btnView1', text: 'ВИД 1 [2]', onClick: ScriptView1 },<br />
        { id: 'btnView2', text: 'ВИД 2 [3]', onClick: ScriptView2 },<br />
        { id: 'btnView3', text: 'ВИД 3 [4]', onClick: ScriptView3 },<br />
    ];</code></div></div><span style="font-family: Montserrat, arial;" class="mycode_font"> 7. Чтобы изменить название цеха нужно поменять этот код. (131-132 строки) </span><br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>    var header = document.createElement("div");<br />
    header.innerText = 'ЦЕХ 058';</code></div></div><span style="font-family: Montserrat, arial;" class="mycode_font"> 8. Чтобы изменить Автооблёт нужно поменять этот код. (333-337 строки)</span><br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>function startAnimation() {<br />
    var startTime = Date.now();<br />
    var radius = 80;<br />
    var height = 60;<br />
    var speed = 0.3;</code></div></div><span style="font-family: Montserrat, arial;" class="mycode_font">9. Чтобы изменить Системную информацию в сплывающем окне нужно поменять этот код. (618-633 строки)</span><br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>    // Текст информации<br />
    var infoText = document.createElement("div");<br />
    infoText.innerHTML = <br />
        '&lt;p style="margin-bottom: 12px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;3D-СЦЕНА:&lt;/strong&gt; ЦЕХ 058' +<br />
        '&lt;/p&gt;' +<br />
        '&lt;p style="margin-bottom: 12px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;РАЗРАБОТЧИК:&lt;/strong&gt; ПОНЯТОВ НИКИТА' +<br />
        '&lt;/p&gt;' +<br />
        '&lt;p style="margin-bottom: 12px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;ВЕРСИЯ:&lt;/strong&gt; 2.0' +<br />
        '&lt;/p&gt;' +<br />
        '&lt;p style="margin-bottom: 15px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;СТАТУС:&lt;/strong&gt; СИСТЕМА АКТИВНА' +<br />
        '&lt;/p&gt;';<br />
    modal.appendChild(infoText);</code></div></div><span style="font-family: Montserrat, arial;" class="mycode_font"> Полный код:</span><br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>var SView, View1, View2, View3, View4;<br />
var currentAnimation = null;<br />
var isNightMode = false;<br />
var frameCount = 0;<br />
var lastTime = performance.now();<br />
var fps = 0;<br />
<br />
// Обновите функции видов для отображения правильных углов<br />
function ScriptSView() {<br />
    console.log('Общий вид активирован');<br />
    camera.position.set(12.51, 77.63, 66.77);<br />
    camera.rotation.set(-52 * Math.PI / 180, 0, 0);<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
    // Убираем вызов updateCoordinates()<br />
    updateStatus('ОБЩИЙ ВИД', '#00ff00');<br />
}<br />
<br />
function ScriptView1() {<br />
    console.log('Вид 1 активирован');<br />
    camera.position.set(-54.87, 31.12, 4.72);<br />
    camera.rotation.set(-44 * Math.PI / 180, 0, 0);<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
    // Убираем вызов updateCoordinates()<br />
    updateStatus('ВИД 1', '#00ff00');<br />
}<br />
<br />
function ScriptView2() {<br />
    console.log('Вид 2 активирован');<br />
    camera.position.set(-23.77, 24.68, -17.76);<br />
    camera.rotation.set(-141 * Math.PI / 180, -37.89 * Math.PI / 180, -153.5 * Math.PI / 180);<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
    // Убираем вызов updateCoordinates()<br />
    updateStatus('ВИД 2', '#00ff00');<br />
}<br />
<br />
function ScriptView3() {<br />
    console.log('Вид 3 активирован');<br />
    camera.position.set(30.25, 52.22, 44.24);<br />
    camera.rotation.set(-58.2 * Math.PI / 180, 0, 0);<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
    // Убираем вызов updateCoordinates()<br />
    updateStatus('ВИД 3', '#00ff00');<br />
}<br />
<br />
function start() {<br />
    createButtonContainer();<br />
    SView = document.getElementById('btnSView');<br />
    View1 = document.getElementById('btnView1');<br />
    View2 = document.getElementById('btnView2');<br />
    View3 = document.getElementById('btnView3');<br />
    View4 = document.getElementById('btnView4');<br />
    initHotkeys();<br />
    // Запускаем подсчет FPS<br />
    requestAnimationFrame(updateFPS);<br />
}<br />
<br />
// Функция для добавления техно-деталей ЧПУ<br />
function addCNCDetails(container) {<br />
    // Добавляем угловые элементы<br />
    const corners = [<br />
        { top: '0', left: '0', transform: 'none' },<br />
        { top: '0', right: '0', transform: 'scaleX(-1)' },<br />
        { bottom: '0', left: '0', transform: 'scaleY(-1)' },<br />
        { bottom: '0', right: '0', transform: 'scale(-1)' }<br />
    ];<br />
<br />
    corners.forEach(corner =&gt; {<br />
        var cornerElement = document.createElement("div");<br />
        cornerElement.style.position = 'absolute';<br />
        cornerElement.style.width = '15px';<br />
        cornerElement.style.height = '15px';<br />
        cornerElement.style.background = 'linear-gradient(135deg, #00cccc, #008080)';<br />
        cornerElement.style.border = '1px solid #00ffff';<br />
        cornerElement.style.boxShadow = '0 0 5px rgba(0, 255, 255, 0.4)';<br />
        cornerElement.style.transform = corner.transform;<br />
        Object.assign(cornerElement.style, corner);<br />
        container.appendChild(cornerElement);<br />
    });<br />
<br />
    // Добавляем техно-сетку на фон<br />
    var gridOverlay = document.createElement("div");<br />
    gridOverlay.style.position = 'absolute';<br />
    gridOverlay.style.top = '0';<br />
    gridOverlay.style.left = '0';<br />
    gridOverlay.style.width = '100%';<br />
    gridOverlay.style.height = '100%';<br />
    gridOverlay.style.backgroundImage = 'linear-gradient(to right, rgba(0, 204, 204, 0.1) 1px, transparent 1px), linear-gradient(to bottom, rgba(0, 204, 204, 0.1) 1px, transparent 1px)';<br />
    gridOverlay.style.backgroundSize = '10px 10px';<br />
    gridOverlay.style.pointerEvents = 'none';<br />
    gridOverlay.style.zIndex = '1';<br />
    container.appendChild(gridOverlay);<br />
}<br />
<br />
function createButtonContainer() {<br />
    var sceneView = document.querySelector("#player &gt; div");<br />
    <br />
    // Создаем основной контейнер в стиле ЧПУ станка<br />
    var mainContainer = document.createElement("div");<br />
    mainContainer.style.position = "absolute";<br />
    mainContainer.style.right = '20px';<br />
    mainContainer.style.bottom = '20px';<br />
    mainContainer.style.zIndex = '1000';<br />
    mainContainer.style.background = 'linear-gradient(145deg, #1e1e1e, #2d2d2d)';<br />
    mainContainer.style.border = '3px solid #404040';<br />
    mainContainer.style.borderRadius = '2px';<br />
    mainContainer.style.padding = '15px';<br />
    mainContainer.style.fontFamily = '"Share Tech Mono", "Courier New", monospace';<br />
    mainContainer.style.minWidth = '250px';<br />
    mainContainer.style.overflow = 'hidden';<br />
    mainContainer.style.boxShadow = '0 0 15px rgba(0, 255, 255, 0.2), inset 0 0 10px rgba(0, 0, 0, 0.5)';<br />
<br />
    // Добавляем техно-детали<br />
    addCNCDetails(mainContainer);<br />
<br />
    // Верхняя панель с названием и статусом<br />
    var headerPlate = document.createElement("div");<br />
    headerPlate.style.background = 'linear-gradient(to bottom, #00b3b3, #008080)';<br />
    headerPlate.style.border = '2px solid #00cccc';<br />
    headerPlate.style.borderRadius = '1px';<br />
    headerPlate.style.padding = '8px 15px';<br />
    headerPlate.style.marginBottom = '15px';<br />
    headerPlate.style.textAlign = 'center';<br />
    headerPlate.style.position = 'relative';<br />
    headerPlate.style.boxShadow = 'inset 0 0 10px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.5)';<br />
    headerPlate.style.textShadow = '0 0 5px rgba(0, 255, 255, 0.7)';<br />
    <br />
    var header = document.createElement("div");<br />
    header.innerText = 'ЦЕХ 058';<br />
    header.style.color = '#00ffff';<br />
    header.style.fontSize = '16px';<br />
    header.style.fontWeight = 'bold';<br />
    header.style.textTransform = 'uppercase';<br />
    header.style.letterSpacing = '3px';<br />
    header.style.marginBottom = '5px';<br />
    <br />
    // Статус бар<br />
    var statusBar = document.createElement("div");<br />
    statusBar.id = 'status-bar';<br />
    statusBar.innerHTML = '&lt;span style="color:#00ff00"&gt;■&lt;/span&gt; СИСТЕМА АКТИВНА';<br />
    statusBar.style.fontSize = '10px';<br />
    statusBar.style.color = '#00ff00';<br />
    <br />
    headerPlate.appendChild(header);<br />
    headerPlate.appendChild(statusBar);<br />
    mainContainer.appendChild(headerPlate);<br />
<br />
    // Контейнер для кнопок<br />
    var btnContainer = document.createElement("div");<br />
    btnContainer.style.display = 'flex';<br />
    btnContainer.style.flexDirection = 'column';<br />
    btnContainer.style.gap = '8px';<br />
    mainContainer.appendChild(btnContainer);<br />
<br />
    // Создаем кнопки видов<br />
    var viewButtons = [<br />
        { id: 'btnSView', text: 'ОБЩИЙ ВИД [1]', onClick: ScriptSView },<br />
        { id: 'btnView1', text: 'ВИД 1 [2]', onClick: ScriptView1 },<br />
        { id: 'btnView2', text: 'ВИД 2 [3]', onClick: ScriptView2 },<br />
        { id: 'btnView3', text: 'ВИД 3 [4]', onClick: ScriptView3 },<br />
    ];<br />
<br />
    viewButtons.forEach(button =&gt; {<br />
        var btn = createCNCButton(button.text, function() { button.onClick(); }, button.id);<br />
        btnContainer.appendChild(btn);<br />
    });<br />
<br />
    // Разделитель<br />
    var separator = document.createElement("div");<br />
    separator.style.height = '1px';<br />
    separator.style.background = 'linear-gradient(to right, transparent, #00cccc, transparent)';<br />
    separator.style.margin = '10px 0';<br />
    separator.style.opacity = '0.5';<br />
    btnContainer.appendChild(separator);<br />
<br />
    // Дополнительные функциональные кнопки<br />
    var funcButtons = [<br />
        { id: 'btnAnimation', text: 'АВТООБЛЕТ [A]', onClick: toggleAnimation },<br />
        { id: 'btnNightMode', text: 'НОЧНОЙ РЕЖИМ [N]', onClick: toggleNightMode },<br />
        { id: 'btnScreenshot', text: 'СКРИНШОТ [P]', onClick: takeScreenshot },<br />
        { id: 'btnReset', text: 'СБРОС [R]', onClick: resetView },<br />
        { id: 'btnInfo', text: 'ИНФО [I]', onClick: showInfoModal }<br />
    ];<br />
<br />
    funcButtons.forEach(button =&gt; {<br />
        var btn = createCNCButton(button.text, button.onClick, button.id);<br />
        if (button.id === 'btnInfo') {<br />
            styleEmergencyButton(btn);<br />
        }<br />
        btnContainer.appendChild(btn);<br />
    });<br />
<br />
// Панель информации (убираем coordDisplay)<br />
var infoPanel = document.createElement("div");<br />
infoPanel.style.marginTop = '15px';<br />
infoPanel.style.padding = '5px';<br />
infoPanel.style.background = 'rgba(0, 0, 0, 0.3)';<br />
infoPanel.style.border = '1px solid #00cccc';<br />
infoPanel.style.borderRadius = '1px';<br />
infoPanel.style.textAlign = 'center';<br />
infoPanel.style.fontSize = '10px';<br />
infoPanel.style.color = '#00cccc';<br />
<br />
// Убираем создание coordDisplay<br />
// var coordDisplay = document.createElement("div");<br />
// coordDisplay.id = 'coord-display';<br />
// coordDisplay.textContent = 'X: 0.0 Y: 0.0 Z: 0.0';<br />
// infoPanel.appendChild(coordDisplay);<br />
<br />
var fpsDisplay = document.createElement("div");<br />
fpsDisplay.id = 'fps-display';<br />
fpsDisplay.textContent = 'FPS: 0';<br />
fpsDisplay.style.marginTop = '3px';<br />
infoPanel.appendChild(fpsDisplay);<br />
<br />
var timeDisplay = document.createElement("div");<br />
timeDisplay.id = 'time-display';<br />
timeDisplay.style.marginTop = '3px';<br />
infoPanel.appendChild(timeDisplay);<br />
<br />
var dateDisplay = document.createElement("div");<br />
dateDisplay.id = 'date-display';<br />
dateDisplay.style.fontSize = '9px';<br />
dateDisplay.style.opacity = '0.7';<br />
dateDisplay.style.marginTop = '3px';<br />
infoPanel.appendChild(dateDisplay);<br />
<br />
btnContainer.appendChild(infoPanel);<br />
<br />
    // Нижняя техно-полоса<br />
    var footerStrip = document.createElement("div");<br />
    footerStrip.style.height = '3px';<br />
    footerStrip.style.background = 'linear-gradient(to right, #00cccc, #008080, #00cccc)';<br />
    footerStrip.style.marginTop = '15px';<br />
    footerStrip.style.borderRadius = '1px';<br />
    footerStrip.style.boxShadow = '0 0 5px rgba(0, 255, 255, 0.3)';<br />
    mainContainer.appendChild(footerStrip);<br />
<br />
    sceneView.appendChild(mainContainer);<br />
    <br />
    // Запускаем обновление времени и координат<br />
    updateTime();<br />
    updateCoordinates();<br />
    setInterval(updateTime, 1000);<br />
    setInterval(updateCoordinates, 100);<br />
}<br />
<br />
// Создание универсальной кнопки<br />
function createCNCButton(text, onClick, id) {<br />
    var btn = document.createElement("BUTTON");<br />
    btn.id = id;<br />
    btn.innerText = text;<br />
    styleCNCButton(btn);<br />
    btn.onclick = onClick;<br />
    return btn;<br />
}<br />
<br />
// Новый функционал: Ночной режим<br />
function toggleNightMode() {<br />
    isNightMode = !isNightMode;<br />
    <br />
    if (isNightMode) {<br />
        // Сохраняем оригинальные настройки освещения<br />
        if (!window.originalLights) {<br />
            window.originalLights = [];<br />
            scene.traverse(function(object) {<br />
                if (object.isLight) {<br />
                    window.originalLights.push({<br />
                        object: object,<br />
                        intensity: object.intensity,<br />
                        visible: object.visible<br />
                    });<br />
                }<br />
            });<br />
        }<br />
        <br />
        // Уменьшаем интенсивность света<br />
        scene.traverse(function(object) {<br />
            if (object.isLight) {<br />
                object.intensity *= 0.3;<br />
            }<br />
        });<br />
        <br />
        // Добавляем синее ночное освещение<br />
        if (!window.nightLight) {<br />
            window.nightLight = new THREE.AmbientLight(0x003366, 0.5);<br />
            scene.add(window.nightLight);<br />
        }<br />
        <br />
        updateStatus('НОЧНОЙ РЕЖИМ', '#0066cc');<br />
    } else {<br />
        // Восстанавливаем оригинальное освещение<br />
        if (window.originalLights) {<br />
            window.originalLights.forEach(light =&gt; {<br />
                light.object.intensity = light.intensity;<br />
                light.object.visible = light.visible;<br />
            });<br />
        }<br />
        <br />
        // Убираем ночное освещение<br />
        if (window.nightLight) {<br />
            scene.remove(window.nightLight);<br />
            window.nightLight = null;<br />
        }<br />
        <br />
        updateStatus('ДНЕВНОЙ РЕЖИМ', '#00ff00');<br />
    }<br />
}<br />
<br />
// Новый функционал: Сброс вида<br />
function resetView() {<br />
    if (currentAnimation) {<br />
        stopAnimation();<br />
    }<br />
    ScriptSView();<br />
    updateStatus('ВИД СБРОШЕН', '#00ff00');<br />
}<br />
<br />
// Новый функционал: АВТООБЛЕТ<br />
function toggleAnimation() {<br />
    if (currentAnimation) {<br />
        stopAnimation();<br />
        updateStatus('АВТООБЛЕТ ОСТАНОВЛЕН', '#ff4444');<br />
    } else {<br />
        startAnimation();<br />
        updateStatus('АВТООБЛЕТ АКТИВЕН', '#00ff00');<br />
    }<br />
}<br />
<br />
function startAnimation() {<br />
    var startTime = Date.now();<br />
    var radius = 80;<br />
    var height = 60;<br />
    var speed = 0.3;<br />
    <br />
    // Сохраняем начальную позицию для возврата<br />
    if (!window.originalCameraPosition) {<br />
        window.originalCameraPosition = camera.position.clone();<br />
        window.originalCameraRotation = camera.rotation.clone();<br />
    }<br />
    <br />
    currentAnimation = function() {<br />
        var time = (Date.now() - startTime) * 0.001 * speed;<br />
        camera.position.x = Math.cos(time) * radius;<br />
        camera.position.z = Math.sin(time) * radius;<br />
        camera.position.y = height + Math.sin(time * 0.5) * 15;<br />
        camera.lookAt(new THREE.Vector3(0, 20, 0));<br />
        camera.updateMatrix();<br />
        <br />
        if (currentAnimation) {<br />
            requestAnimationFrame(currentAnimation);<br />
        }<br />
    };<br />
    currentAnimation();<br />
}<br />
<br />
function stopAnimation() {<br />
    currentAnimation = null;<br />
    // Возвращаем камеру в исходное положение<br />
    if (window.originalCameraPosition) {<br />
        camera.position.copy(window.originalCameraPosition);<br />
        camera.rotation.copy(window.originalCameraRotation);<br />
        camera.updateMatrix();<br />
    }<br />
}<br />
<br />
// Новый функционал: Скриншот<br />
function takeScreenshot() {<br />
    try {<br />
        renderer.render(scene, camera);<br />
        var imageData = renderer.domElement.toDataURL('image/png');<br />
        <br />
        var link = document.createElement('a');<br />
        link.href = imageData;<br />
        link.download = 'cnc_screenshot_' + new Date().toISOString().replace(/:/g, '-') + '.png';<br />
        link.click();<br />
        <br />
        updateStatus('СКРИНШОТ СОХРАНЕН', '#00ff00');<br />
        setTimeout(() =&gt; updateStatus('СИСТЕМА АКТИВНА', '#00ff00'), 2000);<br />
    } catch (error) {<br />
        updateStatus('ОШИБКА СКРИНШОТА', '#ff4444');<br />
    }<br />
}<br />
<br />
// Обновление статус бара<br />
function updateStatus(message, color) {<br />
    var statusBar = document.getElementById('status-bar');<br />
    if (statusBar) {<br />
        statusBar.innerHTML = '&lt;span style="color:' + color + '"&gt;■&lt;/span&gt; ' + message;<br />
        statusBar.style.color = color;<br />
    }<br />
}<br />
<br />
// Обновление времени<br />
function updateTime() {<br />
    var now = new Date();<br />
    var timeDisplay = document.getElementById('time-display');<br />
    var dateDisplay = document.getElementById('date-display');<br />
    <br />
    if (timeDisplay) {<br />
        timeDisplay.textContent = now.toLocaleTimeString();<br />
    }<br />
    if (dateDisplay) {<br />
        dateDisplay.textContent = now.toLocaleDateString();<br />
    }<br />
}<br />
<br />
// Обновление координат камеры<br />
// Альтернатива если THREE.Math.radToDeg не работает<br />
function updateCoordinates() {<br />
    var coordDisplay = document.getElementById('coord-display');<br />
    if (coordDisplay &amp;&amp; camera) {<br />
        // Ручное преобразование радиан в градусы<br />
        var radToDeg = function(rad) { <br />
            return (rad * (180 / Math.PI) + 360) % 360; <br />
        };<br />
        <br />
        var euler = new THREE.Euler();<br />
        euler.setFromQuaternion(camera.quaternion, 'YXZ');<br />
        <br />
        var pitch = radToDeg(euler.x);<br />
        var yaw = radToDeg(euler.y);<br />
        var roll = radToDeg(euler.z);<br />
        <br />
        // Агрессивное округление<br />
        var x = Math.round(camera.position.x);<br />
        var y = Math.round(camera.position.y);<br />
        var z = Math.round(camera.position.z);<br />
        <br />
        coordDisplay.innerHTML = <br />
            '&lt;div style="margin-bottom: 3px; color: #00cccc;"&gt;' +<br />
            'ПОЗИЦИЯ: X: ' + x + ' Y: ' + y + ' Z: ' + z +<br />
            '&lt;/div&gt;' +<br />
            '&lt;div style="color: #00ff99;"&gt;' +<br />
            'ПОВОРОТ: Pitch: ' + Math.round(pitch) + '° ' +<br />
            'Yaw: ' + Math.round(yaw) + '° ' +<br />
            'Roll: ' + Math.round(roll) + '°' +<br />
            '&lt;/div&gt;';<br />
    }<br />
}<br />
<br />
<br />
// Обновление FPS<br />
function updateFPS() {<br />
    frameCount++;<br />
    var currentTime = performance.now();<br />
    <br />
    if (currentTime - lastTime &gt;= 1000) {<br />
        fps = Math.round((frameCount * 1000) / (currentTime - lastTime));<br />
        frameCount = 0;<br />
        lastTime = currentTime;<br />
        <br />
        var fpsDisplay = document.getElementById('fps-display');<br />
        if (fpsDisplay) {<br />
            fpsDisplay.textContent = 'FPS: ' + fps;<br />
            // Меняем цвет в зависимости от FPS<br />
            if (fps &lt; 30) {<br />
                fpsDisplay.style.color = '#ff4444';<br />
            } else if (fps &lt; 50) {<br />
                fpsDisplay.style.color = '#ff9900';<br />
            } else {<br />
                fpsDisplay.style.color = '#00ff00';<br />
            }<br />
        }<br />
    }<br />
    <br />
    requestAnimationFrame(updateFPS);<br />
}<br />
<br />
// Горячие клавиши<br />
function initHotkeys() {<br />
    document.addEventListener('keydown', function(event) {<br />
        switch(event.key.toLowerCase()) {<br />
            case 'a': toggleAnimation(); break;<br />
            case 'n': toggleNightMode(); break;<br />
            case 'p': takeScreenshot(); break;<br />
            case 'r': resetView(); break;<br />
            case 'i': showInfoModal(); break;<br />
            case '1': ScriptSView(); break;<br />
            case '2': ScriptView1(); break;<br />
            case '3': ScriptView2(); break;<br />
            case '4': ScriptView3(); break;<br />
        }<br />
    });<br />
}<br />
<br />
// Функция для стилизации аварийной кнопки INFO<br />
function styleEmergencyButton(btn) {<br />
    btn.style.fontSize = '11px';<br />
    btn.style.padding = '8px 12px';<br />
    btn.style.color = '#fff';<br />
    btn.style.background = 'linear-gradient(to bottom, #ff4444, #cc0000)';<br />
    btn.style.border = '2px solid #ff6666';<br />
    btn.style.borderRadius = '1px';<br />
    btn.style.cursor = 'pointer';<br />
    btn.style.transition = 'all 0.2s ease';<br />
    btn.style.fontWeight = 'bold';<br />
    btn.style.textTransform = 'uppercase';<br />
    btn.style.letterSpacing = '0.5px';<br />
    btn.style.textShadow = '0 0 2px rgba(255, 0, 0, 0.8)';<br />
    btn.style.fontFamily = '"Share Tech Mono", "Courier New", monospace';<br />
    btn.style.boxShadow = 'inset 0 0 3px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.3)';<br />
    btn.style.width = '100%';<br />
    btn.style.marginBottom = '2px';<br />
<br />
    btn.onmouseenter = function () {<br />
        btn.style.background = 'linear-gradient(to bottom, #ff6666, #ff0000)';<br />
        btn.style.boxShadow = 'inset 0 0 5px rgba(0, 0, 0, 0.3), 0 0 8px rgba(255, 0, 0, 0.4)';<br />
        btn.style.transform = 'scale(1.05)';<br />
    };<br />
    <br />
    btn.onmouseleave = function () {<br />
        btn.style.background = 'linear-gradient(to bottom, #ff4444, #cc0000)';<br />
        btn.style.boxShadow = 'inset 0 0 3px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.3)';<br />
        btn.style.transform = 'scale(1)';<br />
    };<br />
    <br />
    btn.onmousedown = function () {<br />
        btn.style.transform = 'scale(0.95)';<br />
        btn.style.boxShadow = 'inset 0 0 5px rgba(0, 0, 0, 0.6), 0 1px 1px rgba(0, 0, 0, 0.2)';<br />
    };<br />
    <br />
    btn.onmouseup = function () {<br />
        btn.style.transform = 'scale(1.05)';<br />
        btn.style.boxShadow = 'inset 0 0 5px rgba(0, 0, 0, 0.3), 0 0 8px rgba(255, 0, 0, 0.4)';<br />
    };<br />
}<br />
<br />
// Стилизация кнопок<br />
function styleCNCButton(btn) {<br />
    btn.style.fontSize = '11px';<br />
    btn.style.padding = '8px 12px';<br />
    btn.style.color = '#00ffff';<br />
    btn.style.background = 'linear-gradient(to bottom, #333, #222)';<br />
    btn.style.border = '2px solid #00cccc';<br />
    btn.style.borderRadius = '1px';<br />
    btn.style.cursor = 'pointer';<br />
    btn.style.transition = 'all 0.2s ease';<br />
    btn.style.fontWeight = 'bold';<br />
    btn.style.textTransform = 'uppercase';<br />
    btn.style.letterSpacing = '0.5px';<br />
    btn.style.textShadow = '0 0 2px rgba(0, 255, 255, 0.8)';<br />
    btn.style.fontFamily = '"Share Tech Mono", "Courier New", monospace';<br />
    btn.style.boxShadow = 'inset 0 0 3px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.3)';<br />
    btn.style.width = '100%';<br />
    btn.style.marginBottom = '2px';<br />
<br />
    btn.onmouseenter = function () {<br />
        btn.style.background = 'linear-gradient(to bottom, #00cccc, #008080)';<br />
        btn.style.color = '#000';<br />
        btn.style.textShadow = '0 0 2px rgba(255, 255, 255, 0.8)';<br />
        btn.style.boxShadow = 'inset 0 0 5px rgba(0, 0, 0, 0.3), 0 0 8px rgba(0, 204, 204, 0.4)';<br />
    };<br />
    <br />
    btn.onmouseleave = function () {<br />
        btn.style.background = 'linear-gradient(to bottom, #333, ' + <br />
            '#222)';<br />
        btn.style.color = '#00ffff';<br />
        btn.style.textShadow = '0 0 2px rgba(0, 255, 255, 0.8)';<br />
        btn.style.boxShadow = 'inset 0 0 3px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.3)';<br />
    };<br />
    <br />
    btn.onmousedown = function () {<br />
        btn.style.transform = 'translateY(1px)';<br />
        btn.style.boxShadow = 'inset 0 0 5px rgba(0, 0, 0, 0.6), 0 1px 1px rgba(0, 0, 0, 0.2)';<br />
    };<br />
    <br />
    btn.onmouseup = function () {<br />
        btn.style.transform = 'translateY(0)';<br />
        btn.style.boxShadow = 'inset 0 0 3px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.3)';<br />
    };<br />
}<br />
<br />
// Функция для показа модального окна в стиле ЧПУ<br />
function showInfoModal() {<br />
    // Создаем overlay<br />
    var overlay = document.createElement("div");<br />
    overlay.style.position = 'fixed';<br />
    overlay.style.top = '0';<br />
    overlay.style.left = '0';<br />
    overlay.style.width = '100%';<br />
    overlay.style.height = '100%';<br />
    overlay.style.background = 'rgba(0, 0, 0, 0.9)';<br />
    overlay.style.zIndex = '2000';<br />
    overlay.style.display = 'flex';<br />
    overlay.style.justifyContent = 'center';<br />
    overlay.style.alignItems = 'center';<br />
    <br />
    // Создаем модальное окно в стиле ЧПУ дисплея<br />
    var modal = document.createElement("div");<br />
    modal.style.background = 'linear-gradient(145deg, #1a1a1a, #2a2a2a)';<br />
    modal.style.border = '3px solid #00cccc';<br />
    modal.style.borderRadius = '2px';<br />
    modal.style.padding = '25px';<br />
    modal.style.maxWidth = '500px';<br />
    modal.style.width = '80%';<br />
    modal.style.boxShadow = '0 0 30px rgba(0, 204, 204, 0.4), inset 0 0 20px rgba(0, 0, 0, 0.6)';<br />
    modal.style.position = 'relative';<br />
    modal.style.color = '#00ffff';<br />
    modal.style.fontFamily = '"Share Tech Mono", "Courier New", monospace';<br />
    modal.style.textShadow = '0 0 3px rgba(0, 255, 255, 0.7)';<br />
<br />
    // Заголовок модального окна<br />
    var title = document.createElement("h2");<br />
    title.textContent = 'СИСТЕМНАЯ ИНФОРМАЦИЯ';<br />
    title.style.color = '#00ff00';<br />
    title.style.marginBottom = '20px';<br />
    title.style.textAlign = 'center';<br />
    title.style.fontSize = '18px';<br />
    title.style.fontWeight = 'bold';<br />
    title.style.textTransform = 'uppercase';<br />
    title.style.letterSpacing = '2px';<br />
    modal.appendChild(title);<br />
    <br />
    // Текст информации<br />
    var infoText = document.createElement("div");<br />
    infoText.innerHTML = <br />
        '&lt;p style="margin-bottom: 12px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;3D-СЦЕНА:&lt;/strong&gt; ЦЕХ 058' +<br />
        '&lt;/p&gt;' +<br />
        '&lt;p style="margin-bottom: 12px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;РАЗРАБОТЧИК:&lt;/strong&gt; ПОНЯТОВ НИКИТА' +<br />
        '&lt;/p&gt;' +<br />
        '&lt;p style="margin-bottom: 12px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;ВЕРСИЯ:&lt;/strong&gt; 2.0' +<br />
        '&lt;/p&gt;' +<br />
        '&lt;p style="margin-bottom: 15px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;СТАТУС:&lt;/strong&gt; СИСТЕМА АКТИВНА' +<br />
        '&lt;/p&gt;';<br />
    modal.appendChild(infoText);<br />
    <br />
    // Контейнер для контактов<br />
    var contactContainer = document.createElement("div");<br />
    contactContainer.style.textAlign = 'center';<br />
    contactContainer.style.marginBottom = '20px';<br />
    contactContainer.style.padding = '12px';<br />
    contactContainer.style.background = 'linear-gradient(145deg, #0d2b2b, #1a4040)';<br />
    contactContainer.style.border = '1px solid #00cccc';<br />
    contactContainer.style.borderRadius = '1px';<br />
    contactContainer.style.boxShadow = 'inset 0 0 10px rgba(0, 0, 0, 0.4)';<br />
    <br />
    var contactTitle = document.createElement("div");<br />
    contactTitle.textContent = 'ТЕХНИЧЕСКАЯ ПОДДЕРЖКА';<br />
    contactTitle.style.color = '#00ff00';<br />
    contactTitle.style.marginBottom = '8px';<br />
    contactTitle.style.fontSize = '12px';<br />
    contactTitle.style.fontWeight = 'bold';<br />
    contactContainer.appendChild(contactTitle);<br />
    <br />
    // Ссылка на email<br />
    var emailLink = document.createElement("a");<br />
    emailLink.href = 'mailto:support@winnum.ru';<br />
    emailLink.textContent = 'support@winnum.ru';<br />
        emailLink.style.color = '#00ffff';<br />
    emailLink.style.textDecoration = 'none';<br />
    emailLink.style.fontWeight = 'bold';<br />
    emailLink.style.fontSize = '14px';<br />
    emailLink.style.padding = '6px 12px';<br />
    emailLink.style.display = 'inline-block';<br />
    emailLink.style.transition = 'all 0.3s ease';<br />
    emailLink.style.border = '1px solid #00cccc';<br />
    emailLink.style.background = 'rgba(0, 204, 204, 0.1)';<br />
    <br />
    emailLink.onmouseenter = function() {<br />
        emailLink.style.background = 'rgba(0, 204, 204, 0.3)';<br />
        emailLink.style.color = '#00ff00';<br />
        emailLink.style.boxShadow = '0 0 10px rgba(0, 204, 204, 0.4)';<br />
    };<br />
    <br />
    emailLink.onmouseleave = function() {<br />
        emailLink.style.background = 'rgba(0, 204, 204, 0.1)';<br />
        emailLink.style.color = '#00ffff';<br />
        emailLink.style.boxShadow = 'none';<br />
    };<br />
    <br />
    contactContainer.appendChild(emailLink);<br />
    modal.appendChild(contactContainer);<br />
    <br />
    // Кнопка закрытия<br />
    var closeButton = document.createElement("button");<br />
    closeButton.textContent = 'ЗАКРЫТЬ [ESC]';<br />
    closeButton.style.background = 'linear-gradient(to bottom, #333, #222)';<br />
    closeButton.style.color = '#00cccc';<br />
    closeButton.style.border = '2px solid #00cccc';<br />
    closeButton.style.padding = '8px 20px';<br />
    closeButton.style.borderRadius = '1px';<br />
    closeButton.style.cursor = 'pointer';<br />
    closeButton.style.fontWeight = 'bold';<br />
    closeButton.style.margin = '0 auto';<br />
    closeButton.style.display = 'block';<br />
    closeButton.style.transition = 'all 0.3s ease';<br />
    closeButton.style.fontFamily = '"Share Tech Mono", "Courier New", monospace';<br />
    closeButton.style.textTransform = 'uppercase';<br />
    <br />
    closeButton.onmouseenter = function() {<br />
        closeButton.style.background = 'linear-gradient(to bottom, #00cccc, #008080)';<br />
        closeButton.style.color = '#000';<br />
    };<br />
    <br />
    closeButton.onmouseleave = function() {<br />
        closeButton.style.background = 'linear-gradient(to bottom, ' + <br />
            '#333, #222)';<br />
        closeButton.style.color = '#00cccc';<br />
    };<br />
    <br />
    closeButton.onclick = function() {<br />
        document.body.removeChild(overlay);<br />
    };<br />
    <br />
    modal.appendChild(closeButton);<br />
    overlay.appendChild(modal);<br />
    <br />
    // Добавляем overlay на страницу<br />
    document.body.appendChild(overlay);<br />
    <br />
    // Закрытие по клику на overlay<br />
    overlay.onclick = function(e) {<br />
        if (e.target === overlay) {<br />
            document.body.removeChild(overlay);<br />
        }<br />
    };<br />
    <br />
    // Закрытие по ESC<br />
    document.addEventListener('keydown', function closeModal(e) {<br />
        if (e.key === 'Escape') {<br />
            document.body.removeChild(overlay);<br />
            document.removeEventListener('keydown', closeModal);<br />
        }<br />
    });<br />
}</code></div></div><span style="font-family: Montserrat, arial;" class="mycode_font"><br />
 </span><br /><!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://community.winnum.io/images/attachtypes/zip.png" title="ZIP File" border="0" alt=".zip" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=60" target="_blank" title="">ViewButtons (2).zip</a> (Размер: 18.75 KB / Загрузок: 2)
<!-- end: postbit_attachments_attachment -->]]></description>
			<content:encoded><![CDATA[<span style="font-family: Montserrat, arial;" class="mycode_font">В приложении Цифровой двойник довольно часто используются кнопки. Я решил создать вторую версию панели с кнопками для 3D в стиле ЧПУ-станка. Здесь будет представлен расширенный функционал.</span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">В данном посте я хотел бы рассказать о том как добавить их себе в 3D-сцену и настроить. Они смогут помочь стилизовать сцену и улучшить восприятие.</span><br />
<br />
<span style="font-family: Montserrat, arial;" class="mycode_font">1. Скачать файл ViewButtons (во вложении) и импортировать себе в сцену (Файл - Импорт).</span><br />
<img src="https://imglink.io/i/5db0a2ab-2729-4c02-b9da-12129cf5eba4.png" loading="lazy"  alt="[Изображение: 5db0a2ab-2729-4c02-b9da-12129cf5eba4.png]" class="mycode_img" /><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">2. Если мы начнём воспроизведение сцены они появятся в правом нижнем углу. Здесь можно увидеть название цеха, индикатор и 9 кнопок: 4 кнопки с видами, Автооблёт, Ночной режим, Скриншот, Сброс кнопка ИНФО. У каждой кнопки есть hotkey и подписан в скобках.</span><br />
<br />
<span style="font-family: Montserrat, arial;" class="mycode_font">Каждая кнопка с видом при нажатии будет перемещать камеру в заданное место. </span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">При Автооблёте камера будет крутиться вокруг сцены.</span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">Ночной режим выключает освещение на сцене и изменяет её в тёмно-синие оттенки.</span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">Скриншот делает снимок экрана, при этом панель с кнопками не будет на нём отображена.</span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">Сброс отменяет действия и/или возвращает камеру на начальную позицию.</span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">Также снизу есть показатель FPS, текущее время и дата.</span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font"><img src="https://imglink.io/i/735e2d8f-bdfe-4376-9bf3-4b924b7a9ecf.png" loading="lazy"  alt="[Изображение: 735e2d8f-bdfe-4376-9bf3-4b924b7a9ecf.png]" class="mycode_img" /></span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">3. Кнопка ИНФО при нажатии откроет всплывающее окно с системной информацией и технической поддержкой. При нажатии на кнопку support@winnum.ru пользователя будет переносить в почту и начинать письмо в support.</span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font"><img src="https://imglink.io/i/4b433863-0d38-474c-8b07-124fee8d7174.png" loading="lazy"  alt="[Изображение: 4b433863-0d38-474c-8b07-124fee8d7174.png]" class="mycode_img" /></span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">4. Внутри ViewButtons в Объект - Действия будет лежать код самой панели.</span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font"><img src="https://imglink.io/i/169d5b16-6e24-4ce7-8048-f329c056a8bc.png" loading="lazy"  alt="[Изображение: 169d5b16-6e24-4ce7-8048-f329c056a8bc.png]" class="mycode_img" /></span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">5. Чтобы настроить кнопки с видами нужно изменить логику для них в коде. (9-47 строки)</span><br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>function ScriptSView() {<br />
    console.log('Общий вид активирован');<br />
    camera.position.set(12.51, 77.63, 66.77);<br />
    camera.rotation.set(-52 * Math.PI / 180, 0, 0);<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
    // Убираем вызов updateCoordinates()<br />
    updateStatus('ОБЩИЙ ВИД', '#00ff00');<br />
}<br />
<br />
function ScriptView1() {<br />
    console.log('Вид 1 активирован');<br />
    camera.position.set(-54.87, 31.12, 4.72);<br />
    camera.rotation.set(-44 * Math.PI / 180, 0, 0);<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
    // Убираем вызов updateCoordinates()<br />
    updateStatus('ВИД 1', '#00ff00');<br />
}<br />
<br />
function ScriptView2() {<br />
    console.log('Вид 2 активирован');<br />
    camera.position.set(-23.77, 24.68, -17.76);<br />
    camera.rotation.set(-141 * Math.PI / 180, -37.89 * Math.PI / 180, -153.5 * Math.PI / 180);<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
    // Убираем вызов updateCoordinates()<br />
    updateStatus('ВИД 2', '#00ff00');<br />
}<br />
<br />
function ScriptView3() {<br />
    console.log('Вид 3 активирован');<br />
    camera.position.set(30.25, 52.22, 44.24);<br />
    camera.rotation.set(-58.2 * Math.PI / 180, 0, 0);<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
    // Убираем вызов updateCoordinates()<br />
    updateStatus('ВИД 3', '#00ff00');<br />
}</code></div></div><span style="font-family: Montserrat, arial;" class="mycode_font">6. Чтобы изменить названия кнопок видов поменять этот код. (158-164 строки) </span><br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>    // Создаем кнопки видов<br />
    var viewButtons = [<br />
        { id: 'btnSView', text: 'ОБЩИЙ ВИД [1]', onClick: ScriptSView },<br />
        { id: 'btnView1', text: 'ВИД 1 [2]', onClick: ScriptView1 },<br />
        { id: 'btnView2', text: 'ВИД 2 [3]', onClick: ScriptView2 },<br />
        { id: 'btnView3', text: 'ВИД 3 [4]', onClick: ScriptView3 },<br />
    ];</code></div></div><span style="font-family: Montserrat, arial;" class="mycode_font"> 7. Чтобы изменить название цеха нужно поменять этот код. (131-132 строки) </span><br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>    var header = document.createElement("div");<br />
    header.innerText = 'ЦЕХ 058';</code></div></div><span style="font-family: Montserrat, arial;" class="mycode_font"> 8. Чтобы изменить Автооблёт нужно поменять этот код. (333-337 строки)</span><br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>function startAnimation() {<br />
    var startTime = Date.now();<br />
    var radius = 80;<br />
    var height = 60;<br />
    var speed = 0.3;</code></div></div><span style="font-family: Montserrat, arial;" class="mycode_font">9. Чтобы изменить Системную информацию в сплывающем окне нужно поменять этот код. (618-633 строки)</span><br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>    // Текст информации<br />
    var infoText = document.createElement("div");<br />
    infoText.innerHTML = <br />
        '&lt;p style="margin-bottom: 12px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;3D-СЦЕНА:&lt;/strong&gt; ЦЕХ 058' +<br />
        '&lt;/p&gt;' +<br />
        '&lt;p style="margin-bottom: 12px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;РАЗРАБОТЧИК:&lt;/strong&gt; ПОНЯТОВ НИКИТА' +<br />
        '&lt;/p&gt;' +<br />
        '&lt;p style="margin-bottom: 12px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;ВЕРСИЯ:&lt;/strong&gt; 2.0' +<br />
        '&lt;/p&gt;' +<br />
        '&lt;p style="margin-bottom: 15px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;СТАТУС:&lt;/strong&gt; СИСТЕМА АКТИВНА' +<br />
        '&lt;/p&gt;';<br />
    modal.appendChild(infoText);</code></div></div><span style="font-family: Montserrat, arial;" class="mycode_font"> Полный код:</span><br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>var SView, View1, View2, View3, View4;<br />
var currentAnimation = null;<br />
var isNightMode = false;<br />
var frameCount = 0;<br />
var lastTime = performance.now();<br />
var fps = 0;<br />
<br />
// Обновите функции видов для отображения правильных углов<br />
function ScriptSView() {<br />
    console.log('Общий вид активирован');<br />
    camera.position.set(12.51, 77.63, 66.77);<br />
    camera.rotation.set(-52 * Math.PI / 180, 0, 0);<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
    // Убираем вызов updateCoordinates()<br />
    updateStatus('ОБЩИЙ ВИД', '#00ff00');<br />
}<br />
<br />
function ScriptView1() {<br />
    console.log('Вид 1 активирован');<br />
    camera.position.set(-54.87, 31.12, 4.72);<br />
    camera.rotation.set(-44 * Math.PI / 180, 0, 0);<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
    // Убираем вызов updateCoordinates()<br />
    updateStatus('ВИД 1', '#00ff00');<br />
}<br />
<br />
function ScriptView2() {<br />
    console.log('Вид 2 активирован');<br />
    camera.position.set(-23.77, 24.68, -17.76);<br />
    camera.rotation.set(-141 * Math.PI / 180, -37.89 * Math.PI / 180, -153.5 * Math.PI / 180);<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
    // Убираем вызов updateCoordinates()<br />
    updateStatus('ВИД 2', '#00ff00');<br />
}<br />
<br />
function ScriptView3() {<br />
    console.log('Вид 3 активирован');<br />
    camera.position.set(30.25, 52.22, 44.24);<br />
    camera.rotation.set(-58.2 * Math.PI / 180, 0, 0);<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
    // Убираем вызов updateCoordinates()<br />
    updateStatus('ВИД 3', '#00ff00');<br />
}<br />
<br />
function start() {<br />
    createButtonContainer();<br />
    SView = document.getElementById('btnSView');<br />
    View1 = document.getElementById('btnView1');<br />
    View2 = document.getElementById('btnView2');<br />
    View3 = document.getElementById('btnView3');<br />
    View4 = document.getElementById('btnView4');<br />
    initHotkeys();<br />
    // Запускаем подсчет FPS<br />
    requestAnimationFrame(updateFPS);<br />
}<br />
<br />
// Функция для добавления техно-деталей ЧПУ<br />
function addCNCDetails(container) {<br />
    // Добавляем угловые элементы<br />
    const corners = [<br />
        { top: '0', left: '0', transform: 'none' },<br />
        { top: '0', right: '0', transform: 'scaleX(-1)' },<br />
        { bottom: '0', left: '0', transform: 'scaleY(-1)' },<br />
        { bottom: '0', right: '0', transform: 'scale(-1)' }<br />
    ];<br />
<br />
    corners.forEach(corner =&gt; {<br />
        var cornerElement = document.createElement("div");<br />
        cornerElement.style.position = 'absolute';<br />
        cornerElement.style.width = '15px';<br />
        cornerElement.style.height = '15px';<br />
        cornerElement.style.background = 'linear-gradient(135deg, #00cccc, #008080)';<br />
        cornerElement.style.border = '1px solid #00ffff';<br />
        cornerElement.style.boxShadow = '0 0 5px rgba(0, 255, 255, 0.4)';<br />
        cornerElement.style.transform = corner.transform;<br />
        Object.assign(cornerElement.style, corner);<br />
        container.appendChild(cornerElement);<br />
    });<br />
<br />
    // Добавляем техно-сетку на фон<br />
    var gridOverlay = document.createElement("div");<br />
    gridOverlay.style.position = 'absolute';<br />
    gridOverlay.style.top = '0';<br />
    gridOverlay.style.left = '0';<br />
    gridOverlay.style.width = '100%';<br />
    gridOverlay.style.height = '100%';<br />
    gridOverlay.style.backgroundImage = 'linear-gradient(to right, rgba(0, 204, 204, 0.1) 1px, transparent 1px), linear-gradient(to bottom, rgba(0, 204, 204, 0.1) 1px, transparent 1px)';<br />
    gridOverlay.style.backgroundSize = '10px 10px';<br />
    gridOverlay.style.pointerEvents = 'none';<br />
    gridOverlay.style.zIndex = '1';<br />
    container.appendChild(gridOverlay);<br />
}<br />
<br />
function createButtonContainer() {<br />
    var sceneView = document.querySelector("#player &gt; div");<br />
    <br />
    // Создаем основной контейнер в стиле ЧПУ станка<br />
    var mainContainer = document.createElement("div");<br />
    mainContainer.style.position = "absolute";<br />
    mainContainer.style.right = '20px';<br />
    mainContainer.style.bottom = '20px';<br />
    mainContainer.style.zIndex = '1000';<br />
    mainContainer.style.background = 'linear-gradient(145deg, #1e1e1e, #2d2d2d)';<br />
    mainContainer.style.border = '3px solid #404040';<br />
    mainContainer.style.borderRadius = '2px';<br />
    mainContainer.style.padding = '15px';<br />
    mainContainer.style.fontFamily = '"Share Tech Mono", "Courier New", monospace';<br />
    mainContainer.style.minWidth = '250px';<br />
    mainContainer.style.overflow = 'hidden';<br />
    mainContainer.style.boxShadow = '0 0 15px rgba(0, 255, 255, 0.2), inset 0 0 10px rgba(0, 0, 0, 0.5)';<br />
<br />
    // Добавляем техно-детали<br />
    addCNCDetails(mainContainer);<br />
<br />
    // Верхняя панель с названием и статусом<br />
    var headerPlate = document.createElement("div");<br />
    headerPlate.style.background = 'linear-gradient(to bottom, #00b3b3, #008080)';<br />
    headerPlate.style.border = '2px solid #00cccc';<br />
    headerPlate.style.borderRadius = '1px';<br />
    headerPlate.style.padding = '8px 15px';<br />
    headerPlate.style.marginBottom = '15px';<br />
    headerPlate.style.textAlign = 'center';<br />
    headerPlate.style.position = 'relative';<br />
    headerPlate.style.boxShadow = 'inset 0 0 10px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.5)';<br />
    headerPlate.style.textShadow = '0 0 5px rgba(0, 255, 255, 0.7)';<br />
    <br />
    var header = document.createElement("div");<br />
    header.innerText = 'ЦЕХ 058';<br />
    header.style.color = '#00ffff';<br />
    header.style.fontSize = '16px';<br />
    header.style.fontWeight = 'bold';<br />
    header.style.textTransform = 'uppercase';<br />
    header.style.letterSpacing = '3px';<br />
    header.style.marginBottom = '5px';<br />
    <br />
    // Статус бар<br />
    var statusBar = document.createElement("div");<br />
    statusBar.id = 'status-bar';<br />
    statusBar.innerHTML = '&lt;span style="color:#00ff00"&gt;■&lt;/span&gt; СИСТЕМА АКТИВНА';<br />
    statusBar.style.fontSize = '10px';<br />
    statusBar.style.color = '#00ff00';<br />
    <br />
    headerPlate.appendChild(header);<br />
    headerPlate.appendChild(statusBar);<br />
    mainContainer.appendChild(headerPlate);<br />
<br />
    // Контейнер для кнопок<br />
    var btnContainer = document.createElement("div");<br />
    btnContainer.style.display = 'flex';<br />
    btnContainer.style.flexDirection = 'column';<br />
    btnContainer.style.gap = '8px';<br />
    mainContainer.appendChild(btnContainer);<br />
<br />
    // Создаем кнопки видов<br />
    var viewButtons = [<br />
        { id: 'btnSView', text: 'ОБЩИЙ ВИД [1]', onClick: ScriptSView },<br />
        { id: 'btnView1', text: 'ВИД 1 [2]', onClick: ScriptView1 },<br />
        { id: 'btnView2', text: 'ВИД 2 [3]', onClick: ScriptView2 },<br />
        { id: 'btnView3', text: 'ВИД 3 [4]', onClick: ScriptView3 },<br />
    ];<br />
<br />
    viewButtons.forEach(button =&gt; {<br />
        var btn = createCNCButton(button.text, function() { button.onClick(); }, button.id);<br />
        btnContainer.appendChild(btn);<br />
    });<br />
<br />
    // Разделитель<br />
    var separator = document.createElement("div");<br />
    separator.style.height = '1px';<br />
    separator.style.background = 'linear-gradient(to right, transparent, #00cccc, transparent)';<br />
    separator.style.margin = '10px 0';<br />
    separator.style.opacity = '0.5';<br />
    btnContainer.appendChild(separator);<br />
<br />
    // Дополнительные функциональные кнопки<br />
    var funcButtons = [<br />
        { id: 'btnAnimation', text: 'АВТООБЛЕТ [A]', onClick: toggleAnimation },<br />
        { id: 'btnNightMode', text: 'НОЧНОЙ РЕЖИМ [N]', onClick: toggleNightMode },<br />
        { id: 'btnScreenshot', text: 'СКРИНШОТ [P]', onClick: takeScreenshot },<br />
        { id: 'btnReset', text: 'СБРОС [R]', onClick: resetView },<br />
        { id: 'btnInfo', text: 'ИНФО [I]', onClick: showInfoModal }<br />
    ];<br />
<br />
    funcButtons.forEach(button =&gt; {<br />
        var btn = createCNCButton(button.text, button.onClick, button.id);<br />
        if (button.id === 'btnInfo') {<br />
            styleEmergencyButton(btn);<br />
        }<br />
        btnContainer.appendChild(btn);<br />
    });<br />
<br />
// Панель информации (убираем coordDisplay)<br />
var infoPanel = document.createElement("div");<br />
infoPanel.style.marginTop = '15px';<br />
infoPanel.style.padding = '5px';<br />
infoPanel.style.background = 'rgba(0, 0, 0, 0.3)';<br />
infoPanel.style.border = '1px solid #00cccc';<br />
infoPanel.style.borderRadius = '1px';<br />
infoPanel.style.textAlign = 'center';<br />
infoPanel.style.fontSize = '10px';<br />
infoPanel.style.color = '#00cccc';<br />
<br />
// Убираем создание coordDisplay<br />
// var coordDisplay = document.createElement("div");<br />
// coordDisplay.id = 'coord-display';<br />
// coordDisplay.textContent = 'X: 0.0 Y: 0.0 Z: 0.0';<br />
// infoPanel.appendChild(coordDisplay);<br />
<br />
var fpsDisplay = document.createElement("div");<br />
fpsDisplay.id = 'fps-display';<br />
fpsDisplay.textContent = 'FPS: 0';<br />
fpsDisplay.style.marginTop = '3px';<br />
infoPanel.appendChild(fpsDisplay);<br />
<br />
var timeDisplay = document.createElement("div");<br />
timeDisplay.id = 'time-display';<br />
timeDisplay.style.marginTop = '3px';<br />
infoPanel.appendChild(timeDisplay);<br />
<br />
var dateDisplay = document.createElement("div");<br />
dateDisplay.id = 'date-display';<br />
dateDisplay.style.fontSize = '9px';<br />
dateDisplay.style.opacity = '0.7';<br />
dateDisplay.style.marginTop = '3px';<br />
infoPanel.appendChild(dateDisplay);<br />
<br />
btnContainer.appendChild(infoPanel);<br />
<br />
    // Нижняя техно-полоса<br />
    var footerStrip = document.createElement("div");<br />
    footerStrip.style.height = '3px';<br />
    footerStrip.style.background = 'linear-gradient(to right, #00cccc, #008080, #00cccc)';<br />
    footerStrip.style.marginTop = '15px';<br />
    footerStrip.style.borderRadius = '1px';<br />
    footerStrip.style.boxShadow = '0 0 5px rgba(0, 255, 255, 0.3)';<br />
    mainContainer.appendChild(footerStrip);<br />
<br />
    sceneView.appendChild(mainContainer);<br />
    <br />
    // Запускаем обновление времени и координат<br />
    updateTime();<br />
    updateCoordinates();<br />
    setInterval(updateTime, 1000);<br />
    setInterval(updateCoordinates, 100);<br />
}<br />
<br />
// Создание универсальной кнопки<br />
function createCNCButton(text, onClick, id) {<br />
    var btn = document.createElement("BUTTON");<br />
    btn.id = id;<br />
    btn.innerText = text;<br />
    styleCNCButton(btn);<br />
    btn.onclick = onClick;<br />
    return btn;<br />
}<br />
<br />
// Новый функционал: Ночной режим<br />
function toggleNightMode() {<br />
    isNightMode = !isNightMode;<br />
    <br />
    if (isNightMode) {<br />
        // Сохраняем оригинальные настройки освещения<br />
        if (!window.originalLights) {<br />
            window.originalLights = [];<br />
            scene.traverse(function(object) {<br />
                if (object.isLight) {<br />
                    window.originalLights.push({<br />
                        object: object,<br />
                        intensity: object.intensity,<br />
                        visible: object.visible<br />
                    });<br />
                }<br />
            });<br />
        }<br />
        <br />
        // Уменьшаем интенсивность света<br />
        scene.traverse(function(object) {<br />
            if (object.isLight) {<br />
                object.intensity *= 0.3;<br />
            }<br />
        });<br />
        <br />
        // Добавляем синее ночное освещение<br />
        if (!window.nightLight) {<br />
            window.nightLight = new THREE.AmbientLight(0x003366, 0.5);<br />
            scene.add(window.nightLight);<br />
        }<br />
        <br />
        updateStatus('НОЧНОЙ РЕЖИМ', '#0066cc');<br />
    } else {<br />
        // Восстанавливаем оригинальное освещение<br />
        if (window.originalLights) {<br />
            window.originalLights.forEach(light =&gt; {<br />
                light.object.intensity = light.intensity;<br />
                light.object.visible = light.visible;<br />
            });<br />
        }<br />
        <br />
        // Убираем ночное освещение<br />
        if (window.nightLight) {<br />
            scene.remove(window.nightLight);<br />
            window.nightLight = null;<br />
        }<br />
        <br />
        updateStatus('ДНЕВНОЙ РЕЖИМ', '#00ff00');<br />
    }<br />
}<br />
<br />
// Новый функционал: Сброс вида<br />
function resetView() {<br />
    if (currentAnimation) {<br />
        stopAnimation();<br />
    }<br />
    ScriptSView();<br />
    updateStatus('ВИД СБРОШЕН', '#00ff00');<br />
}<br />
<br />
// Новый функционал: АВТООБЛЕТ<br />
function toggleAnimation() {<br />
    if (currentAnimation) {<br />
        stopAnimation();<br />
        updateStatus('АВТООБЛЕТ ОСТАНОВЛЕН', '#ff4444');<br />
    } else {<br />
        startAnimation();<br />
        updateStatus('АВТООБЛЕТ АКТИВЕН', '#00ff00');<br />
    }<br />
}<br />
<br />
function startAnimation() {<br />
    var startTime = Date.now();<br />
    var radius = 80;<br />
    var height = 60;<br />
    var speed = 0.3;<br />
    <br />
    // Сохраняем начальную позицию для возврата<br />
    if (!window.originalCameraPosition) {<br />
        window.originalCameraPosition = camera.position.clone();<br />
        window.originalCameraRotation = camera.rotation.clone();<br />
    }<br />
    <br />
    currentAnimation = function() {<br />
        var time = (Date.now() - startTime) * 0.001 * speed;<br />
        camera.position.x = Math.cos(time) * radius;<br />
        camera.position.z = Math.sin(time) * radius;<br />
        camera.position.y = height + Math.sin(time * 0.5) * 15;<br />
        camera.lookAt(new THREE.Vector3(0, 20, 0));<br />
        camera.updateMatrix();<br />
        <br />
        if (currentAnimation) {<br />
            requestAnimationFrame(currentAnimation);<br />
        }<br />
    };<br />
    currentAnimation();<br />
}<br />
<br />
function stopAnimation() {<br />
    currentAnimation = null;<br />
    // Возвращаем камеру в исходное положение<br />
    if (window.originalCameraPosition) {<br />
        camera.position.copy(window.originalCameraPosition);<br />
        camera.rotation.copy(window.originalCameraRotation);<br />
        camera.updateMatrix();<br />
    }<br />
}<br />
<br />
// Новый функционал: Скриншот<br />
function takeScreenshot() {<br />
    try {<br />
        renderer.render(scene, camera);<br />
        var imageData = renderer.domElement.toDataURL('image/png');<br />
        <br />
        var link = document.createElement('a');<br />
        link.href = imageData;<br />
        link.download = 'cnc_screenshot_' + new Date().toISOString().replace(/:/g, '-') + '.png';<br />
        link.click();<br />
        <br />
        updateStatus('СКРИНШОТ СОХРАНЕН', '#00ff00');<br />
        setTimeout(() =&gt; updateStatus('СИСТЕМА АКТИВНА', '#00ff00'), 2000);<br />
    } catch (error) {<br />
        updateStatus('ОШИБКА СКРИНШОТА', '#ff4444');<br />
    }<br />
}<br />
<br />
// Обновление статус бара<br />
function updateStatus(message, color) {<br />
    var statusBar = document.getElementById('status-bar');<br />
    if (statusBar) {<br />
        statusBar.innerHTML = '&lt;span style="color:' + color + '"&gt;■&lt;/span&gt; ' + message;<br />
        statusBar.style.color = color;<br />
    }<br />
}<br />
<br />
// Обновление времени<br />
function updateTime() {<br />
    var now = new Date();<br />
    var timeDisplay = document.getElementById('time-display');<br />
    var dateDisplay = document.getElementById('date-display');<br />
    <br />
    if (timeDisplay) {<br />
        timeDisplay.textContent = now.toLocaleTimeString();<br />
    }<br />
    if (dateDisplay) {<br />
        dateDisplay.textContent = now.toLocaleDateString();<br />
    }<br />
}<br />
<br />
// Обновление координат камеры<br />
// Альтернатива если THREE.Math.radToDeg не работает<br />
function updateCoordinates() {<br />
    var coordDisplay = document.getElementById('coord-display');<br />
    if (coordDisplay &amp;&amp; camera) {<br />
        // Ручное преобразование радиан в градусы<br />
        var radToDeg = function(rad) { <br />
            return (rad * (180 / Math.PI) + 360) % 360; <br />
        };<br />
        <br />
        var euler = new THREE.Euler();<br />
        euler.setFromQuaternion(camera.quaternion, 'YXZ');<br />
        <br />
        var pitch = radToDeg(euler.x);<br />
        var yaw = radToDeg(euler.y);<br />
        var roll = radToDeg(euler.z);<br />
        <br />
        // Агрессивное округление<br />
        var x = Math.round(camera.position.x);<br />
        var y = Math.round(camera.position.y);<br />
        var z = Math.round(camera.position.z);<br />
        <br />
        coordDisplay.innerHTML = <br />
            '&lt;div style="margin-bottom: 3px; color: #00cccc;"&gt;' +<br />
            'ПОЗИЦИЯ: X: ' + x + ' Y: ' + y + ' Z: ' + z +<br />
            '&lt;/div&gt;' +<br />
            '&lt;div style="color: #00ff99;"&gt;' +<br />
            'ПОВОРОТ: Pitch: ' + Math.round(pitch) + '° ' +<br />
            'Yaw: ' + Math.round(yaw) + '° ' +<br />
            'Roll: ' + Math.round(roll) + '°' +<br />
            '&lt;/div&gt;';<br />
    }<br />
}<br />
<br />
<br />
// Обновление FPS<br />
function updateFPS() {<br />
    frameCount++;<br />
    var currentTime = performance.now();<br />
    <br />
    if (currentTime - lastTime &gt;= 1000) {<br />
        fps = Math.round((frameCount * 1000) / (currentTime - lastTime));<br />
        frameCount = 0;<br />
        lastTime = currentTime;<br />
        <br />
        var fpsDisplay = document.getElementById('fps-display');<br />
        if (fpsDisplay) {<br />
            fpsDisplay.textContent = 'FPS: ' + fps;<br />
            // Меняем цвет в зависимости от FPS<br />
            if (fps &lt; 30) {<br />
                fpsDisplay.style.color = '#ff4444';<br />
            } else if (fps &lt; 50) {<br />
                fpsDisplay.style.color = '#ff9900';<br />
            } else {<br />
                fpsDisplay.style.color = '#00ff00';<br />
            }<br />
        }<br />
    }<br />
    <br />
    requestAnimationFrame(updateFPS);<br />
}<br />
<br />
// Горячие клавиши<br />
function initHotkeys() {<br />
    document.addEventListener('keydown', function(event) {<br />
        switch(event.key.toLowerCase()) {<br />
            case 'a': toggleAnimation(); break;<br />
            case 'n': toggleNightMode(); break;<br />
            case 'p': takeScreenshot(); break;<br />
            case 'r': resetView(); break;<br />
            case 'i': showInfoModal(); break;<br />
            case '1': ScriptSView(); break;<br />
            case '2': ScriptView1(); break;<br />
            case '3': ScriptView2(); break;<br />
            case '4': ScriptView3(); break;<br />
        }<br />
    });<br />
}<br />
<br />
// Функция для стилизации аварийной кнопки INFO<br />
function styleEmergencyButton(btn) {<br />
    btn.style.fontSize = '11px';<br />
    btn.style.padding = '8px 12px';<br />
    btn.style.color = '#fff';<br />
    btn.style.background = 'linear-gradient(to bottom, #ff4444, #cc0000)';<br />
    btn.style.border = '2px solid #ff6666';<br />
    btn.style.borderRadius = '1px';<br />
    btn.style.cursor = 'pointer';<br />
    btn.style.transition = 'all 0.2s ease';<br />
    btn.style.fontWeight = 'bold';<br />
    btn.style.textTransform = 'uppercase';<br />
    btn.style.letterSpacing = '0.5px';<br />
    btn.style.textShadow = '0 0 2px rgba(255, 0, 0, 0.8)';<br />
    btn.style.fontFamily = '"Share Tech Mono", "Courier New", monospace';<br />
    btn.style.boxShadow = 'inset 0 0 3px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.3)';<br />
    btn.style.width = '100%';<br />
    btn.style.marginBottom = '2px';<br />
<br />
    btn.onmouseenter = function () {<br />
        btn.style.background = 'linear-gradient(to bottom, #ff6666, #ff0000)';<br />
        btn.style.boxShadow = 'inset 0 0 5px rgba(0, 0, 0, 0.3), 0 0 8px rgba(255, 0, 0, 0.4)';<br />
        btn.style.transform = 'scale(1.05)';<br />
    };<br />
    <br />
    btn.onmouseleave = function () {<br />
        btn.style.background = 'linear-gradient(to bottom, #ff4444, #cc0000)';<br />
        btn.style.boxShadow = 'inset 0 0 3px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.3)';<br />
        btn.style.transform = 'scale(1)';<br />
    };<br />
    <br />
    btn.onmousedown = function () {<br />
        btn.style.transform = 'scale(0.95)';<br />
        btn.style.boxShadow = 'inset 0 0 5px rgba(0, 0, 0, 0.6), 0 1px 1px rgba(0, 0, 0, 0.2)';<br />
    };<br />
    <br />
    btn.onmouseup = function () {<br />
        btn.style.transform = 'scale(1.05)';<br />
        btn.style.boxShadow = 'inset 0 0 5px rgba(0, 0, 0, 0.3), 0 0 8px rgba(255, 0, 0, 0.4)';<br />
    };<br />
}<br />
<br />
// Стилизация кнопок<br />
function styleCNCButton(btn) {<br />
    btn.style.fontSize = '11px';<br />
    btn.style.padding = '8px 12px';<br />
    btn.style.color = '#00ffff';<br />
    btn.style.background = 'linear-gradient(to bottom, #333, #222)';<br />
    btn.style.border = '2px solid #00cccc';<br />
    btn.style.borderRadius = '1px';<br />
    btn.style.cursor = 'pointer';<br />
    btn.style.transition = 'all 0.2s ease';<br />
    btn.style.fontWeight = 'bold';<br />
    btn.style.textTransform = 'uppercase';<br />
    btn.style.letterSpacing = '0.5px';<br />
    btn.style.textShadow = '0 0 2px rgba(0, 255, 255, 0.8)';<br />
    btn.style.fontFamily = '"Share Tech Mono", "Courier New", monospace';<br />
    btn.style.boxShadow = 'inset 0 0 3px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.3)';<br />
    btn.style.width = '100%';<br />
    btn.style.marginBottom = '2px';<br />
<br />
    btn.onmouseenter = function () {<br />
        btn.style.background = 'linear-gradient(to bottom, #00cccc, #008080)';<br />
        btn.style.color = '#000';<br />
        btn.style.textShadow = '0 0 2px rgba(255, 255, 255, 0.8)';<br />
        btn.style.boxShadow = 'inset 0 0 5px rgba(0, 0, 0, 0.3), 0 0 8px rgba(0, 204, 204, 0.4)';<br />
    };<br />
    <br />
    btn.onmouseleave = function () {<br />
        btn.style.background = 'linear-gradient(to bottom, #333, ' + <br />
            '#222)';<br />
        btn.style.color = '#00ffff';<br />
        btn.style.textShadow = '0 0 2px rgba(0, 255, 255, 0.8)';<br />
        btn.style.boxShadow = 'inset 0 0 3px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.3)';<br />
    };<br />
    <br />
    btn.onmousedown = function () {<br />
        btn.style.transform = 'translateY(1px)';<br />
        btn.style.boxShadow = 'inset 0 0 5px rgba(0, 0, 0, 0.6), 0 1px 1px rgba(0, 0, 0, 0.2)';<br />
    };<br />
    <br />
    btn.onmouseup = function () {<br />
        btn.style.transform = 'translateY(0)';<br />
        btn.style.boxShadow = 'inset 0 0 3px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.3)';<br />
    };<br />
}<br />
<br />
// Функция для показа модального окна в стиле ЧПУ<br />
function showInfoModal() {<br />
    // Создаем overlay<br />
    var overlay = document.createElement("div");<br />
    overlay.style.position = 'fixed';<br />
    overlay.style.top = '0';<br />
    overlay.style.left = '0';<br />
    overlay.style.width = '100%';<br />
    overlay.style.height = '100%';<br />
    overlay.style.background = 'rgba(0, 0, 0, 0.9)';<br />
    overlay.style.zIndex = '2000';<br />
    overlay.style.display = 'flex';<br />
    overlay.style.justifyContent = 'center';<br />
    overlay.style.alignItems = 'center';<br />
    <br />
    // Создаем модальное окно в стиле ЧПУ дисплея<br />
    var modal = document.createElement("div");<br />
    modal.style.background = 'linear-gradient(145deg, #1a1a1a, #2a2a2a)';<br />
    modal.style.border = '3px solid #00cccc';<br />
    modal.style.borderRadius = '2px';<br />
    modal.style.padding = '25px';<br />
    modal.style.maxWidth = '500px';<br />
    modal.style.width = '80%';<br />
    modal.style.boxShadow = '0 0 30px rgba(0, 204, 204, 0.4), inset 0 0 20px rgba(0, 0, 0, 0.6)';<br />
    modal.style.position = 'relative';<br />
    modal.style.color = '#00ffff';<br />
    modal.style.fontFamily = '"Share Tech Mono", "Courier New", monospace';<br />
    modal.style.textShadow = '0 0 3px rgba(0, 255, 255, 0.7)';<br />
<br />
    // Заголовок модального окна<br />
    var title = document.createElement("h2");<br />
    title.textContent = 'СИСТЕМНАЯ ИНФОРМАЦИЯ';<br />
    title.style.color = '#00ff00';<br />
    title.style.marginBottom = '20px';<br />
    title.style.textAlign = 'center';<br />
    title.style.fontSize = '18px';<br />
    title.style.fontWeight = 'bold';<br />
    title.style.textTransform = 'uppercase';<br />
    title.style.letterSpacing = '2px';<br />
    modal.appendChild(title);<br />
    <br />
    // Текст информации<br />
    var infoText = document.createElement("div");<br />
    infoText.innerHTML = <br />
        '&lt;p style="margin-bottom: 12px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;3D-СЦЕНА:&lt;/strong&gt; ЦЕХ 058' +<br />
        '&lt;/p&gt;' +<br />
        '&lt;p style="margin-bottom: 12px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;РАЗРАБОТЧИК:&lt;/strong&gt; ПОНЯТОВ НИКИТА' +<br />
        '&lt;/p&gt;' +<br />
        '&lt;p style="margin-bottom: 12px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;ВЕРСИЯ:&lt;/strong&gt; 2.0' +<br />
        '&lt;/p&gt;' +<br />
        '&lt;p style="margin-bottom: 15px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;СТАТУС:&lt;/strong&gt; СИСТЕМА АКТИВНА' +<br />
        '&lt;/p&gt;';<br />
    modal.appendChild(infoText);<br />
    <br />
    // Контейнер для контактов<br />
    var contactContainer = document.createElement("div");<br />
    contactContainer.style.textAlign = 'center';<br />
    contactContainer.style.marginBottom = '20px';<br />
    contactContainer.style.padding = '12px';<br />
    contactContainer.style.background = 'linear-gradient(145deg, #0d2b2b, #1a4040)';<br />
    contactContainer.style.border = '1px solid #00cccc';<br />
    contactContainer.style.borderRadius = '1px';<br />
    contactContainer.style.boxShadow = 'inset 0 0 10px rgba(0, 0, 0, 0.4)';<br />
    <br />
    var contactTitle = document.createElement("div");<br />
    contactTitle.textContent = 'ТЕХНИЧЕСКАЯ ПОДДЕРЖКА';<br />
    contactTitle.style.color = '#00ff00';<br />
    contactTitle.style.marginBottom = '8px';<br />
    contactTitle.style.fontSize = '12px';<br />
    contactTitle.style.fontWeight = 'bold';<br />
    contactContainer.appendChild(contactTitle);<br />
    <br />
    // Ссылка на email<br />
    var emailLink = document.createElement("a");<br />
    emailLink.href = 'mailto:support@winnum.ru';<br />
    emailLink.textContent = 'support@winnum.ru';<br />
        emailLink.style.color = '#00ffff';<br />
    emailLink.style.textDecoration = 'none';<br />
    emailLink.style.fontWeight = 'bold';<br />
    emailLink.style.fontSize = '14px';<br />
    emailLink.style.padding = '6px 12px';<br />
    emailLink.style.display = 'inline-block';<br />
    emailLink.style.transition = 'all 0.3s ease';<br />
    emailLink.style.border = '1px solid #00cccc';<br />
    emailLink.style.background = 'rgba(0, 204, 204, 0.1)';<br />
    <br />
    emailLink.onmouseenter = function() {<br />
        emailLink.style.background = 'rgba(0, 204, 204, 0.3)';<br />
        emailLink.style.color = '#00ff00';<br />
        emailLink.style.boxShadow = '0 0 10px rgba(0, 204, 204, 0.4)';<br />
    };<br />
    <br />
    emailLink.onmouseleave = function() {<br />
        emailLink.style.background = 'rgba(0, 204, 204, 0.1)';<br />
        emailLink.style.color = '#00ffff';<br />
        emailLink.style.boxShadow = 'none';<br />
    };<br />
    <br />
    contactContainer.appendChild(emailLink);<br />
    modal.appendChild(contactContainer);<br />
    <br />
    // Кнопка закрытия<br />
    var closeButton = document.createElement("button");<br />
    closeButton.textContent = 'ЗАКРЫТЬ [ESC]';<br />
    closeButton.style.background = 'linear-gradient(to bottom, #333, #222)';<br />
    closeButton.style.color = '#00cccc';<br />
    closeButton.style.border = '2px solid #00cccc';<br />
    closeButton.style.padding = '8px 20px';<br />
    closeButton.style.borderRadius = '1px';<br />
    closeButton.style.cursor = 'pointer';<br />
    closeButton.style.fontWeight = 'bold';<br />
    closeButton.style.margin = '0 auto';<br />
    closeButton.style.display = 'block';<br />
    closeButton.style.transition = 'all 0.3s ease';<br />
    closeButton.style.fontFamily = '"Share Tech Mono", "Courier New", monospace';<br />
    closeButton.style.textTransform = 'uppercase';<br />
    <br />
    closeButton.onmouseenter = function() {<br />
        closeButton.style.background = 'linear-gradient(to bottom, #00cccc, #008080)';<br />
        closeButton.style.color = '#000';<br />
    };<br />
    <br />
    closeButton.onmouseleave = function() {<br />
        closeButton.style.background = 'linear-gradient(to bottom, ' + <br />
            '#333, #222)';<br />
        closeButton.style.color = '#00cccc';<br />
    };<br />
    <br />
    closeButton.onclick = function() {<br />
        document.body.removeChild(overlay);<br />
    };<br />
    <br />
    modal.appendChild(closeButton);<br />
    overlay.appendChild(modal);<br />
    <br />
    // Добавляем overlay на страницу<br />
    document.body.appendChild(overlay);<br />
    <br />
    // Закрытие по клику на overlay<br />
    overlay.onclick = function(e) {<br />
        if (e.target === overlay) {<br />
            document.body.removeChild(overlay);<br />
        }<br />
    };<br />
    <br />
    // Закрытие по ESC<br />
    document.addEventListener('keydown', function closeModal(e) {<br />
        if (e.key === 'Escape') {<br />
            document.body.removeChild(overlay);<br />
            document.removeEventListener('keydown', closeModal);<br />
        }<br />
    });<br />
}</code></div></div><span style="font-family: Montserrat, arial;" class="mycode_font"><br />
 </span><br /><!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://community.winnum.io/images/attachtypes/zip.png" title="ZIP File" border="0" alt=".zip" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=60" target="_blank" title="">ViewButtons (2).zip</a> (Размер: 18.75 KB / Загрузок: 2)
<!-- end: postbit_attachments_attachment -->]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Панель с кнопками для 3D v1]]></title>
			<link>https://community.winnum.io/showthread.php?tid=45</link>
			<pubDate>Fri, 19 Sep 2025 11:31:28 +0300</pubDate>
			<dc:creator><![CDATA[<a href="https://community.winnum.io/member.php?action=profile&uid=95">nponyatov</a>]]></dc:creator>
			<guid isPermaLink="false">https://community.winnum.io/showthread.php?tid=45</guid>
			<description><![CDATA[<span style="font-family: Montserrat, arial;" class="mycode_font">В приложении Цифровой двойник довольно часто используются кнопки. Я решил создать панель с кнопками для 3D в стиле ЧПУ-станка. </span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">В данном посте я хотел бы рассказать о том как добавить их себе в 3D-сцену и настроить. Они смогут помочь стилизовать сцену и улучшить восприятие.</span><br />
<br />
<span style="font-family: Montserrat, arial;" class="mycode_font">1. Скачать файл ViewButtons (во вложении) и импортировать себе в сцену (Файл - Импорт).</span><br />
<img src="https://imglink.io/i/5db0a2ab-2729-4c02-b9da-12129cf5eba4.png" loading="lazy"  alt="[Изображение: 5db0a2ab-2729-4c02-b9da-12129cf5eba4.png]" class="mycode_img" /><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">2. Если мы начнём воспроизведение сцены они появятся в правом нижнем углу. Здесь можно увидеть название цеха и 5 кнопок: 4 кнопки с видами, кнопка INFO. Каждая кнопка с видом при нажатии будет перемещать камеру в заданное место. </span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font"><img src="https://imglink.io/i/62588b92-1006-48ef-a65b-7e2f21572f1a.png" loading="lazy"  alt="[Изображение: 62588b92-1006-48ef-a65b-7e2f21572f1a.png]" class="mycode_img" /></span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">3. Кнопка INFO при нажатии откроет всплывающее окно с системной информацией и технической поддержкой. При нажатии на кнопку support@winnum.ru пользователя будет переносить в почту и начинать письмо в support.</span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font"><img src="https://imglink.io/i/fc3682c2-550a-46e4-825d-d6620a63d12f.png" loading="lazy"  alt="[Изображение: fc3682c2-550a-46e4-825d-d6620a63d12f.png]" class="mycode_img" /></span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">4. Внутри ViewButtons в Объект - Действия будет лежать код самой панели.</span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font"><img src="https://imglink.io/i/169d5b16-6e24-4ce7-8048-f329c056a8bc.png" loading="lazy"  alt="[Изображение: 169d5b16-6e24-4ce7-8048-f329c056a8bc.png]" class="mycode_img" /></span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">5. Чтобы настроить кнопки с видами нужно изменить логику для них в коде в параметрах по camera.position и camera.rotation. (386-436 строки)</span><br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>// Логика для общего вида<br />
function ScriptSView(cButton) {<br />
    console.log('Общий вид активирован');<br />
    camera.position.x = 1.76;<br />
    camera.position.y = 49.84;<br />
    camera.position.z = 39;<br />
    camera.rotation.x = -52.42 * Math.PI / 180;<br />
    camera.rotation.y = 0 * Math.PI / 180;<br />
    camera.rotation.z = 0 * Math.PI / 180;<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
}<br />
// Логика для вида 1<br />
function ScriptView1(cButton) {<br />
    console.log('Вид 1 активирован');<br />
    camera.position.x = -41;<br />
    camera.position.y = 32.43;<br />
    camera.position.z = 3.4;<br />
    camera.rotation.x = -58 * Math.PI / 180;<br />
    camera.rotation.y = -37.42 * Math.PI / 180;<br />
    camera.rotation.z = -44.3 * Math.PI / 180;<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
}<br />
// Логика для вида 2<br />
function ScriptView2(cButton) {<br />
    console.log('Вид 2 активирован');<br />
    camera.position.x = 27.72;<br />
    camera.position.y = 40.57;<br />
    camera.position.z = 37.53;<br />
    camera.rotation.x = -47.26 * Math.PI / 180;<br />
    camera.rotation.y = 9.58 * Math.PI / 180;<br />
    camera.rotation.z = 10.21 * Math.PI / 180;<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
}<br />
// Логика для вида 3<br />
function ScriptView3(cButton) {<br />
    console.log('Вид 3 активирован');<br />
    camera.position.x = -53.74;<br />
    camera.position.y = 28.2;<br />
    camera.position.z = 30.92;<br />
    camera.rotation.x = -44.4 * Math.PI / 180;<br />
    camera.rotation.y = -43.24 * Math.PI / 180;<br />
    camera.rotation.z = -33.86 * Math.PI / 180;<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
}</code></div></div><span style="font-family: Montserrat, arial;" class="mycode_font">6. Чтобы изменить названия кнопок нужно поменять этот код. (65-71 строки)</span><br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>    // Создаем кнопки видов<br />
    var buttons = [<br />
        { id: 'btnSView', text: 'ОБЩИЙ ВИД', onClick: ScriptSView },<br />
        { id: 'btnView1', text: 'ВИД 1', onClick: ScriptView1 },<br />
        { id: 'btnView2', text: 'ВИД 2', onClick: ScriptView2 },<br />
        { id: 'btnView3', text: 'ВИД 3', onClick: ScriptView3 },<br />
    ];</code></div></div><span style="font-family: Montserrat, arial;" class="mycode_font">7. Чтобы изменить название цеха нужно поменять этот код. (45-47 строки)</span><br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>    // Заголовок "ЦЕХ 9" в стиле ЧПУ дисплея<br />
    var header = document.createElement("div");<br />
    header.innerText = 'ЦЕХ 9';</code></div></div><span style="font-family: Montserrat, arial;" class="mycode_font">8. Чтобы изменить Системную информацию в сплывающем окне нужно поменять этот код. (273-285 строки)</span><br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>    // Текст информации<br />
    var infoText = document.createElement("div");<br />
    infoText.innerHTML = <br />
        '&lt;p style="margin-bottom: 12px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;3D-СЦЕНА:&lt;/strong&gt; ЦЕХ 9' +<br />
        '&lt;/p&gt;' +<br />
        '&lt;p style="margin-bottom: 12px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;РАЗРАБОТЧИК:&lt;/strong&gt; ПОНЯТОВ НИКИТА' +<br />
        '&lt;/p&gt;' +<br />
        '&lt;p style="margin-bottom: 15px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;СТАТУС:&lt;/strong&gt; СИСТЕМА АКТИВНА' +<br />
        '&lt;/p&gt;';<br />
    modal.appendChild(infoText);</code></div></div><br />
Полный код:<br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>var SView, View1, View2, View3, View4;<br />
<br />
function start() {<br />
    createButtonContainer();<br />
    SView = document.getElementById('btnSView');<br />
    View1 = document.getElementById('btnView1');<br />
    View2 = document.getElementById('btnView2');<br />
    View3 = document.getElementById('btnView3');<br />
    View4 = document.getElementById('btnView4');<br />
}<br />
<br />
function createButtonContainer() {<br />
    var sceneView = document.querySelector("#player &gt; div");<br />
    <br />
    // Создаем основной контейнер в стиле ЧПУ станка<br />
    var mainContainer = document.createElement("div");<br />
    mainContainer.style.position = "absolute";<br />
    mainContainer.style.right = '20px';<br />
    mainContainer.style.bottom = '20px';<br />
    mainContainer.style.zIndex = '1000';<br />
    mainContainer.style.background = 'linear-gradient(145deg, #1e1e1e, #2d2d2d)';<br />
    mainContainer.style.border = '3px solid #404040';<br />
    mainContainer.style.borderRadius = '2px';<br />
    mainContainer.style.padding = '15px';<br />
    mainContainer.style.fontFamily = '"Share Tech Mono", "Courier New", monospace';<br />
    mainContainer.style.minWidth = '220px';<br />
    mainContainer.style.overflow = 'hidden';<br />
    mainContainer.style.boxShadow = '0 0 15px rgba(0, 255, 255, 0.2), inset 0 0 10px rgba(0, 0, 0, 0.5)';<br />
<br />
    // Добавляем техно-детали<br />
    addCNCDetails(mainContainer);<br />
<br />
    // Верхняя панель с названием в стиле ЧПУ дисплея<br />
    var headerPlate = document.createElement("div");<br />
    headerPlate.style.background = 'linear-gradient(to bottom, #00b3b3, #008080)';<br />
    headerPlate.style.border = '2px solid #00cccc';<br />
    headerPlate.style.borderRadius = '1px';<br />
    headerPlate.style.padding = '8px 15px';<br />
    headerPlate.style.marginBottom = '15px';<br />
    headerPlate.style.textAlign = 'center';<br />
    headerPlate.style.position = 'relative';<br />
    headerPlate.style.boxShadow = 'inset 0 0 10px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.5)';<br />
    headerPlate.style.textShadow = '0 0 5px rgba(0, 255, 255, 0.7)';<br />
    <br />
    // Заголовок "ЦЕХ 9" в стиле ЧПУ дисплея<br />
    var header = document.createElement("div");<br />
    header.innerText = 'ЦЕХ 9';<br />
    header.style.color = '#00ffff';<br />
    header.style.fontSize = '16px';<br />
    header.style.fontWeight = 'bold';<br />
    header.style.textTransform = 'uppercase';<br />
    header.style.letterSpacing = '3px';<br />
    header.style.fontFamily = '"Share Tech Mono", "Courier New", monospace';<br />
    <br />
    headerPlate.appendChild(header);<br />
    mainContainer.appendChild(headerPlate);<br />
<br />
    // Контейнер для кнопок<br />
    var btnContainer = document.createElement("div");<br />
    btnContainer.style.display = 'flex';<br />
    btnContainer.style.flexDirection = 'column';<br />
    btnContainer.style.gap = '8px';<br />
    mainContainer.appendChild(btnContainer);<br />
<br />
    // Создаем кнопки видов<br />
    var buttons = [<br />
        { id: 'btnSView', text: 'ОБЩИЙ ВИД', onClick: ScriptSView },<br />
        { id: 'btnView1', text: 'ВИД 1', onClick: ScriptView1 },<br />
        { id: 'btnView2', text: 'ВИД 2', onClick: ScriptView2 },<br />
        { id: 'btnView3', text: 'ВИД 3', onClick: ScriptView3 },<br />
    ];<br />
<br />
    buttons.forEach(button =&gt; {<br />
        var btn = document.createElement("BUTTON");<br />
        btn.name = button.id;<br />
        btn.id = button.id;<br />
        btn.innerText = button.text;<br />
        styleCNCButton(btn);<br />
        btn.onclick = function () { button.onClick(btn); };<br />
        btnContainer.appendChild(btn);<br />
    });<br />
<br />
    // Кнопка INFO в стиле аварийной кнопки<br />
    var infoButton = document.createElement("BUTTON");<br />
    infoButton.name = 'btnInfo';<br />
    infoButton.id = 'btnInfo';<br />
    infoButton.innerText = 'INFO';<br />
    styleEmergencyButton(infoButton);<br />
    infoButton.onclick = function () { showInfoModal(); };<br />
    btnContainer.appendChild(infoButton);<br />
<br />
    // Нижняя техно-полоса<br />
    var footerStrip = document.createElement("div");<br />
    footerStrip.style.height = '3px';<br />
    footerStrip.style.background = 'linear-gradient(to right, #00cccc, #008080, #00cccc)';<br />
    footerStrip.style.marginTop = '15px';<br />
    footerStrip.style.borderRadius = '1px';<br />
    footerStrip.style.boxShadow = '0 0 5px rgba(0, 255, 255, 0.3)';<br />
    mainContainer.appendChild(footerStrip);<br />
<br />
    sceneView.appendChild(mainContainer);<br />
}<br />
<br />
// Функция для добавления техно-деталей ЧПУ<br />
function addCNCDetails(container) {<br />
    // Добавляем угловые элементы<br />
    const corners = [<br />
        { top: '0', left: '0', transform: 'none' },<br />
        { top: '0', right: '0', transform: 'scaleX(-1)' },<br />
        { bottom: '0', left: '0', transform: 'scaleY(-1)' },<br />
        { bottom: '0', right: '0', transform: 'scale(-1)' }<br />
    ];<br />
<br />
    corners.forEach(corner =&gt; {<br />
        var cornerElement = document.createElement("div");<br />
        cornerElement.style.position = 'absolute';<br />
        cornerElement.style.width = '15px';<br />
        cornerElement.style.height = '15px';<br />
        cornerElement.style.background = 'linear-gradient(135deg, #00cccc, #008080)';<br />
        cornerElement.style.border = '1px solid #00ffff';<br />
        cornerElement.style.boxShadow = '0 0 5px rgba(0, 255, 255, 0.4)';<br />
        cornerElement.style.transform = corner.transform;<br />
        Object.assign(cornerElement.style, corner);<br />
        container.appendChild(cornerElement);<br />
    });<br />
<br />
    // Добавляем техно-сетку на фон<br />
    var gridOverlay = document.createElement("div");<br />
    gridOverlay.style.position = 'absolute';<br />
    gridOverlay.style.top = '0';<br />
    gridOverlay.style.left = '0';<br />
    gridOverlay.style.width = '100%';<br />
    gridOverlay.style.height = '100%';<br />
    gridOverlay.style.backgroundImage = 'linear-gradient(to right, rgba(0, 204, 204, 0.1) 1px, transparent 1px), linear-gradient(to bottom, rgba(0, 204, 204, 0.1) 1px, transparent 1px)';<br />
    gridOverlay.style.backgroundSize = '10px 10px';<br />
    gridOverlay.style.pointerEvents = 'none';<br />
    gridOverlay.style.zIndex = '1';<br />
    container.appendChild(gridOverlay);<br />
}<br />
<br />
// Функция для стилизации обычных кнопок ЧПУ<br />
function styleCNCButton(btn) {<br />
    btn.style.fontSize = '12px';<br />
    btn.style.padding = '10px 15px';<br />
    btn.style.color = '#00ffff';<br />
    btn.style.background = 'linear-gradient(to bottom, #333, #222)';<br />
    btn.style.border = '2px solid #00cccc';<br />
    btn.style.borderRadius = '1px';<br />
    btn.style.cursor = 'pointer';<br />
    btn.style.transition = 'all 0.2s ease';<br />
    btn.style.fontWeight = 'bold';<br />
    btn.style.textTransform = 'uppercase';<br />
    btn.style.letterSpacing = '1px';<br />
    btn.style.textShadow = '0 0 3px rgba(0, 255, 255, 0.8)';<br />
    btn.style.fontFamily = '"Share Tech Mono", "Courier New", monospace';<br />
    btn.style.boxShadow = 'inset 0 0 5px rgba(0, 0, 0, 0.5), 0 2px 4px rgba(0, 0, 0, 0.3)';<br />
<br />
    // Эффект на hover<br />
    btn.onmouseenter = function () {<br />
        btn.style.background = 'linear-gradient(to bottom, #00cccc, #008080)';<br />
        btn.style.color = '#000';<br />
        btn.style.textShadow = '0 0 3px rgba(255, 255, 255, 0.8)';<br />
        btn.style.boxShadow = 'inset 0 0 10px rgba(0, 0, 0, 0.3), 0 0 15px rgba(0, 204, 204, 0.4)';<br />
    };<br />
    <br />
    btn.onmouseleave = function () {<br />
        btn.style.background = 'linear-gradient(to bottom, #333, #222)';<br />
        btn.style.color = '#00ffff';<br />
        btn.style.textShadow = '0 0 3px rgba(0, 255, 255, 0.8)';<br />
        btn.style.boxShadow = 'inset 0 0 5px rgba(0, 0, 0, 0.5), 0 2px 4px rgba(0, 0, 0, 0.3)';<br />
    };<br />
    <br />
    // Эффект при нажатии<br />
    btn.onmousedown = function () {<br />
        btn.style.transform = 'translateY(1px)';<br />
        btn.style.boxShadow = 'inset 0 0 8px rgba(0, 0, 0, 0.6), 0 1px 2px rgba(0, 0, 0, 0.3)';<br />
    };<br />
    <br />
    btn.onmouseup = function () {<br />
        btn.style.transform = 'translateY(0)';<br />
        btn.style.boxShadow = 'inset 0 0 5px rgba(0, 0, 0, 0.5), 0 2px 4px rgba(0, 0, 0, 0.3)';<br />
    };<br />
}<br />
<br />
// Функция для стилизации аварийной кнопки INFO<br />
function styleEmergencyButton(btn) {<br />
    btn.style.fontSize = '12px';<br />
    btn.style.padding = '10px 15px';<br />
    btn.style.color = '#fff';<br />
    btn.style.background = 'linear-gradient(to bottom, #ff4444, #cc0000)';<br />
    btn.style.border = '2px solid #ff6666';<br />
    btn.style.borderRadius = '1px';<br />
    btn.style.cursor = 'pointer';<br />
    btn.style.transition = 'all 0.2s ease';<br />
    btn.style.fontWeight = 'bold';<br />
    btn.style.textTransform = 'uppercase';<br />
    btn.style.letterSpacing = '1px';<br />
    btn.style.textShadow = '0 0 3px rgba(255, 0, 0, 0.8)';<br />
    btn.style.fontFamily = '"Share Tech Mono", "Courier New", monospace';<br />
    btn.style.boxShadow = 'inset 0 0 5px rgba(0, 0, 0, 0.5), 0 2px 4px rgba(0, 0, 0, 0.3)';<br />
    btn.style.marginTop = '10px';<br />
<br />
    // Эффект на hover<br />
    btn.onmouseenter = function () {<br />
        btn.style.background = 'linear-gradient(to bottom, #ff6666, #ff0000)';<br />
        btn.style.boxShadow = 'inset 0 0 10px rgba(0, 0, 0, 0.3), 0 0 15px rgba(255, 0, 0, 0.4)';<br />
        btn.style.transform = 'scale(1.05)';<br />
    };<br />
    <br />
    btn.onmouseleave = function () {<br />
        btn.style.background = 'linear-gradient(to bottom, #ff4444, #cc0000)';<br />
        btn.style.boxShadow = 'inset 0 0 5px rgba(0, 0, 0, 0.5), 0 2px 4px rgba(0, 0, 0, 0.3)';<br />
        btn.style.transform = 'scale(1)';<br />
    };<br />
    <br />
    // Эффект при нажатии<br />
    btn.onmousedown = function () {<br />
        btn.style.transform = 'scale(0.95)';<br />
        btn.style.boxShadow = 'inset 0 0 8px rgba(0, 0, 0, 0.6), 0 1px 2px rgba(0, 0, 0, 0.3)';<br />
    };<br />
    <br />
    btn.onmouseup = function () {<br />
        btn.style.transform = 'scale(1.05)';<br />
        btn.style.boxShadow = 'inset 0 0 10px rgba(0, 0, 0, 0.3), 0 0 15px rgba(255, 0, 0, 0.4)';<br />
    };<br />
}<br />
<br />
// Функция для показа модального окна в стиле ЧПУ<br />
function showInfoModal() {<br />
    // Создаем overlay<br />
    var overlay = document.createElement("div");<br />
    overlay.style.position = 'fixed';<br />
    overlay.style.top = '0';<br />
    overlay.style.left = '0';<br />
    overlay.style.width = '100%';<br />
    overlay.style.height = '100%';<br />
    overlay.style.background = 'rgba(0, 0, 0, 0.9)';<br />
    overlay.style.zIndex = '2000';<br />
    overlay.style.display = 'flex';<br />
    overlay.style.justifyContent = 'center';<br />
    overlay.style.alignItems = 'center';<br />
    overlay.style.backdropFilter = 'blur(3px)';<br />
    <br />
    // Создаем модальное окно в стиле ЧПУ дисплея<br />
    var modal = document.createElement("div");<br />
    modal.style.background = 'linear-gradient(145deg, #1a1a1a, #2a2a2a)';<br />
    modal.style.border = '3px solid #00cccc';<br />
    modal.style.borderRadius = '2px';<br />
    modal.style.padding = '25px';<br />
    modal.style.maxWidth = '500px';<br />
    modal.style.width = '80%';<br />
    modal.style.boxShadow = '0 0 30px rgba(0, 204, 204, 0.4), inset 0 0 20px rgba(0, 0, 0, 0.6)';<br />
    modal.style.position = 'relative';<br />
    modal.style.color = '#00ffff';<br />
    modal.style.fontFamily = '"Share Tech Mono", "Courier New", monospace';<br />
    modal.style.textShadow = '0 0 3px rgba(0, 255, 255, 0.7)';<br />
<br />
    // Добавляем техно-детали к модальному окну<br />
    addCNCDetails(modal);<br />
<br />
    // Заголовок модального окна<br />
    var title = document.createElement("h2");<br />
    title.textContent = 'СИСТЕМНАЯ ИНФОРМАЦИЯ';<br />
    title.style.color = '#00ff00';<br />
    title.style.marginBottom = '20px';<br />
    title.style.textAlign = 'center';<br />
    title.style.fontSize = '18px';<br />
    title.style.fontWeight = 'bold';<br />
    title.style.textTransform = 'uppercase';<br />
    title.style.letterSpacing = '2px';<br />
    modal.appendChild(title);<br />
    <br />
    // Текст информации<br />
    var infoText = document.createElement("div");<br />
    infoText.innerHTML = <br />
        '&lt;p style="margin-bottom: 12px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;3D-СЦЕНА:&lt;/strong&gt; ЦЕХ 9' +<br />
        '&lt;/p&gt;' +<br />
        '&lt;p style="margin-bottom: 12px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;РАЗРАБОТЧИК:&lt;/strong&gt; ПОНЯТОВ НИКИТА' +<br />
        '&lt;/p&gt;' +<br />
        '&lt;p style="margin-bottom: 15px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;СТАТУС:&lt;/strong&gt; СИСТЕМА АКТИВНА' +<br />
        '&lt;/p&gt;';<br />
    modal.appendChild(infoText);<br />
    <br />
    // Контейнер для контактов<br />
    var contactContainer = document.createElement("div");<br />
    contactContainer.style.textAlign = 'center';<br />
    contactContainer.style.marginBottom = '20px';<br />
    contactContainer.style.padding = '12px';<br />
    contactContainer.style.background = 'linear-gradient(145deg, #0d2b2b, #1a4040)';<br />
    contactContainer.style.border = '1px solid #00cccc';<br />
    contactContainer.style.borderRadius = '1px';<br />
    contactContainer.style.boxShadow = 'inset 0 0 10px rgba(0, 0, 0, 0.4)';<br />
    <br />
    var contactTitle = document.createElement("div");<br />
    contactTitle.textContent = 'ТЕХНИЧЕСКАЯ ПОДДЕРЖКА';<br />
    contactTitle.style.color = '#00ff00';<br />
    contactTitle.style.marginBottom = '8px';<br />
    contactTitle.style.fontSize = '12px';<br />
    contactTitle.style.fontWeight = 'bold';<br />
    contactContainer.appendChild(contactTitle);<br />
    <br />
    // Ссылка на email<br />
    var emailLink = document.createElement("a");<br />
    emailLink.href = 'mailto:support@winnum.ru';<br />
    emailLink.textContent = 'support@winnum.ru';<br />
    emailLink.style.color = '#00ffff';<br />
    emailLink.style.textDecoration = 'none';<br />
    emailLink.style.fontWeight = 'bold';<br />
    emailLink.style.fontSize = '14px';<br />
    emailLink.style.padding = '6px 12px';<br />
    emailLink.style.display = 'inline-block';<br />
    emailLink.style.transition = 'all 0.3s ease';<br />
    emailLink.style.border = '1px solid #00cccc';<br />
    emailLink.style.background = 'rgba(0, 204, 204, 0.1)';<br />
    <br />
    emailLink.onmouseenter = function() {<br />
        emailLink.style.background = 'rgba(0, 204, 204, 0.3)';<br />
        emailLink.style.color = '#00ff00';<br />
        emailLink.style.boxShadow = '0 0 10px rgba(0, 204, 204, 0.4)';<br />
    };<br />
    <br />
    emailLink.onmouseleave = function() {<br />
        emailLink.style.background = 'rgba(0, 204, 204, 0.1)';<br />
        emailLink.style.color = '#00ffff';<br />
        emailLink.style.boxShadow = 'none';<br />
    };<br />
    <br />
    contactContainer.appendChild(emailLink);<br />
    modal.appendChild(contactContainer);<br />
    <br />
    // Кнопка закрытия<br />
    var closeButton = document.createElement("button");<br />
    closeButton.textContent = 'ЗАКРЫТЬ [ESC]';<br />
    closeButton.style.background = 'linear-gradient(to bottom, #333, #222)';<br />
    closeButton.style.color = '#00cccc';<br />
    closeButton.style.border = '2px solid #00cccc';<br />
    closeButton.style.padding = '8px 20px';<br />
    closeButton.style.borderRadius = '1px';<br />
    closeButton.style.cursor = 'pointer';<br />
    closeButton.style.fontWeight = 'bold';<br />
    closeButton.style.margin = '0 auto';<br />
    closeButton.style.display = 'block';<br />
    closeButton.style.transition = 'all 0.3s ease';<br />
    closeButton.style.fontFamily = '"Share Tech Mono", "Courier New", monospace';<br />
    closeButton.style.textTransform = 'uppercase';<br />
    <br />
    closeButton.onmouseenter = function() {<br />
        closeButton.style.background = 'linear-gradient(to bottom, #00cccc, #008080)';<br />
        closeButton.style.color = '#000';<br />
    };<br />
    <br />
    closeButton.onmouseleave = function() {<br />
        closeButton.style.background = 'linear-gradient(to bottom, #333, #222)';<br />
        closeButton.style.color = '#00cccc';<br />
    };<br />
    <br />
    closeButton.onclick = function() {<br />
        document.body.removeChild(overlay);<br />
    };<br />
    <br />
    modal.appendChild(closeButton);<br />
    overlay.appendChild(modal);<br />
    <br />
    // Добавляем overlay на страницу<br />
    document.body.appendChild(overlay);<br />
    <br />
    // Закрытие по клику на overlay<br />
    overlay.onclick = function(e) {<br />
        if (e.target === overlay) {<br />
            document.body.removeChild(overlay);<br />
        }<br />
    };<br />
    <br />
    // Закрытие по ESC<br />
    document.addEventListener('keydown', function closeModal(e) {<br />
        if (e.key === 'Escape') {<br />
            document.body.removeChild(overlay);<br />
            document.removeEventListener('keydown', closeModal);<br />
        }<br />
    });<br />
}<br />
<br />
// Логика для общего вида<br />
function ScriptSView(cButton) {<br />
    console.log('Общий вид активирован');<br />
    camera.position.x = 1.76;<br />
    camera.position.y = 49.84;<br />
    camera.position.z = 39;<br />
    camera.rotation.x = -52.42 * Math.PI / 180;<br />
    camera.rotation.y = 0 * Math.PI / 180;<br />
    camera.rotation.z = 0 * Math.PI / 180;<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
}<br />
<br />
// Логика для вида 1<br />
function ScriptView1(cButton) {<br />
    console.log('Вид 1 активирован');<br />
    camera.position.x = -41;<br />
    camera.position.y = 32.43;<br />
    camera.position.z = 3.4;<br />
    camera.rotation.x = -58 * Math.PI / 180;<br />
    camera.rotation.y = -37.42 * Math.PI / 180;<br />
    camera.rotation.z = -44.3 * Math.PI / 180;<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
}<br />
<br />
// Логика для вида 2<br />
function ScriptView2(cButton) {<br />
    console.log('Вид 2 активирован');<br />
    camera.position.x = 27.72;<br />
    camera.position.y = 40.57;<br />
    camera.position.z = 37.53;<br />
    camera.rotation.x = -47.26 * Math.PI / 180;<br />
    camera.rotation.y = 9.58 * Math.PI / 180;<br />
    camera.rotation.z = 10.21 * Math.PI / 180;<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
}<br />
<br />
// Логика для вида 3<br />
function ScriptView3(cButton) {<br />
    console.log('Вид 3 активирован');<br />
    camera.position.x = -53.74;<br />
    camera.position.y = 28.2;<br />
    camera.position.z = 30.92;<br />
    camera.rotation.x = -44.4 * Math.PI / 180;<br />
    camera.rotation.y = -43.24 * Math.PI / 180;<br />
    camera.rotation.z = -33.86 * Math.PI / 180;<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
}</code></div></div><br /><!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://community.winnum.io/images/attachtypes/zip.png" title="ZIP File" border="0" alt=".zip" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=59" target="_blank" title="">ViewButtons (2).zip</a> (Размер: 18.8 KB / Загрузок: 1)
<!-- end: postbit_attachments_attachment -->]]></description>
			<content:encoded><![CDATA[<span style="font-family: Montserrat, arial;" class="mycode_font">В приложении Цифровой двойник довольно часто используются кнопки. Я решил создать панель с кнопками для 3D в стиле ЧПУ-станка. </span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">В данном посте я хотел бы рассказать о том как добавить их себе в 3D-сцену и настроить. Они смогут помочь стилизовать сцену и улучшить восприятие.</span><br />
<br />
<span style="font-family: Montserrat, arial;" class="mycode_font">1. Скачать файл ViewButtons (во вложении) и импортировать себе в сцену (Файл - Импорт).</span><br />
<img src="https://imglink.io/i/5db0a2ab-2729-4c02-b9da-12129cf5eba4.png" loading="lazy"  alt="[Изображение: 5db0a2ab-2729-4c02-b9da-12129cf5eba4.png]" class="mycode_img" /><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">2. Если мы начнём воспроизведение сцены они появятся в правом нижнем углу. Здесь можно увидеть название цеха и 5 кнопок: 4 кнопки с видами, кнопка INFO. Каждая кнопка с видом при нажатии будет перемещать камеру в заданное место. </span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font"><img src="https://imglink.io/i/62588b92-1006-48ef-a65b-7e2f21572f1a.png" loading="lazy"  alt="[Изображение: 62588b92-1006-48ef-a65b-7e2f21572f1a.png]" class="mycode_img" /></span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">3. Кнопка INFO при нажатии откроет всплывающее окно с системной информацией и технической поддержкой. При нажатии на кнопку support@winnum.ru пользователя будет переносить в почту и начинать письмо в support.</span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font"><img src="https://imglink.io/i/fc3682c2-550a-46e4-825d-d6620a63d12f.png" loading="lazy"  alt="[Изображение: fc3682c2-550a-46e4-825d-d6620a63d12f.png]" class="mycode_img" /></span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">4. Внутри ViewButtons в Объект - Действия будет лежать код самой панели.</span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font"><img src="https://imglink.io/i/169d5b16-6e24-4ce7-8048-f329c056a8bc.png" loading="lazy"  alt="[Изображение: 169d5b16-6e24-4ce7-8048-f329c056a8bc.png]" class="mycode_img" /></span><br />
<span style="font-family: Montserrat, arial;" class="mycode_font">5. Чтобы настроить кнопки с видами нужно изменить логику для них в коде в параметрах по camera.position и camera.rotation. (386-436 строки)</span><br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>// Логика для общего вида<br />
function ScriptSView(cButton) {<br />
    console.log('Общий вид активирован');<br />
    camera.position.x = 1.76;<br />
    camera.position.y = 49.84;<br />
    camera.position.z = 39;<br />
    camera.rotation.x = -52.42 * Math.PI / 180;<br />
    camera.rotation.y = 0 * Math.PI / 180;<br />
    camera.rotation.z = 0 * Math.PI / 180;<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
}<br />
// Логика для вида 1<br />
function ScriptView1(cButton) {<br />
    console.log('Вид 1 активирован');<br />
    camera.position.x = -41;<br />
    camera.position.y = 32.43;<br />
    camera.position.z = 3.4;<br />
    camera.rotation.x = -58 * Math.PI / 180;<br />
    camera.rotation.y = -37.42 * Math.PI / 180;<br />
    camera.rotation.z = -44.3 * Math.PI / 180;<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
}<br />
// Логика для вида 2<br />
function ScriptView2(cButton) {<br />
    console.log('Вид 2 активирован');<br />
    camera.position.x = 27.72;<br />
    camera.position.y = 40.57;<br />
    camera.position.z = 37.53;<br />
    camera.rotation.x = -47.26 * Math.PI / 180;<br />
    camera.rotation.y = 9.58 * Math.PI / 180;<br />
    camera.rotation.z = 10.21 * Math.PI / 180;<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
}<br />
// Логика для вида 3<br />
function ScriptView3(cButton) {<br />
    console.log('Вид 3 активирован');<br />
    camera.position.x = -53.74;<br />
    camera.position.y = 28.2;<br />
    camera.position.z = 30.92;<br />
    camera.rotation.x = -44.4 * Math.PI / 180;<br />
    camera.rotation.y = -43.24 * Math.PI / 180;<br />
    camera.rotation.z = -33.86 * Math.PI / 180;<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
}</code></div></div><span style="font-family: Montserrat, arial;" class="mycode_font">6. Чтобы изменить названия кнопок нужно поменять этот код. (65-71 строки)</span><br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>    // Создаем кнопки видов<br />
    var buttons = [<br />
        { id: 'btnSView', text: 'ОБЩИЙ ВИД', onClick: ScriptSView },<br />
        { id: 'btnView1', text: 'ВИД 1', onClick: ScriptView1 },<br />
        { id: 'btnView2', text: 'ВИД 2', onClick: ScriptView2 },<br />
        { id: 'btnView3', text: 'ВИД 3', onClick: ScriptView3 },<br />
    ];</code></div></div><span style="font-family: Montserrat, arial;" class="mycode_font">7. Чтобы изменить название цеха нужно поменять этот код. (45-47 строки)</span><br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>    // Заголовок "ЦЕХ 9" в стиле ЧПУ дисплея<br />
    var header = document.createElement("div");<br />
    header.innerText = 'ЦЕХ 9';</code></div></div><span style="font-family: Montserrat, arial;" class="mycode_font">8. Чтобы изменить Системную информацию в сплывающем окне нужно поменять этот код. (273-285 строки)</span><br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>    // Текст информации<br />
    var infoText = document.createElement("div");<br />
    infoText.innerHTML = <br />
        '&lt;p style="margin-bottom: 12px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;3D-СЦЕНА:&lt;/strong&gt; ЦЕХ 9' +<br />
        '&lt;/p&gt;' +<br />
        '&lt;p style="margin-bottom: 12px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;РАЗРАБОТЧИК:&lt;/strong&gt; ПОНЯТОВ НИКИТА' +<br />
        '&lt;/p&gt;' +<br />
        '&lt;p style="margin-bottom: 15px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;СТАТУС:&lt;/strong&gt; СИСТЕМА АКТИВНА' +<br />
        '&lt;/p&gt;';<br />
    modal.appendChild(infoText);</code></div></div><br />
Полный код:<br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>var SView, View1, View2, View3, View4;<br />
<br />
function start() {<br />
    createButtonContainer();<br />
    SView = document.getElementById('btnSView');<br />
    View1 = document.getElementById('btnView1');<br />
    View2 = document.getElementById('btnView2');<br />
    View3 = document.getElementById('btnView3');<br />
    View4 = document.getElementById('btnView4');<br />
}<br />
<br />
function createButtonContainer() {<br />
    var sceneView = document.querySelector("#player &gt; div");<br />
    <br />
    // Создаем основной контейнер в стиле ЧПУ станка<br />
    var mainContainer = document.createElement("div");<br />
    mainContainer.style.position = "absolute";<br />
    mainContainer.style.right = '20px';<br />
    mainContainer.style.bottom = '20px';<br />
    mainContainer.style.zIndex = '1000';<br />
    mainContainer.style.background = 'linear-gradient(145deg, #1e1e1e, #2d2d2d)';<br />
    mainContainer.style.border = '3px solid #404040';<br />
    mainContainer.style.borderRadius = '2px';<br />
    mainContainer.style.padding = '15px';<br />
    mainContainer.style.fontFamily = '"Share Tech Mono", "Courier New", monospace';<br />
    mainContainer.style.minWidth = '220px';<br />
    mainContainer.style.overflow = 'hidden';<br />
    mainContainer.style.boxShadow = '0 0 15px rgba(0, 255, 255, 0.2), inset 0 0 10px rgba(0, 0, 0, 0.5)';<br />
<br />
    // Добавляем техно-детали<br />
    addCNCDetails(mainContainer);<br />
<br />
    // Верхняя панель с названием в стиле ЧПУ дисплея<br />
    var headerPlate = document.createElement("div");<br />
    headerPlate.style.background = 'linear-gradient(to bottom, #00b3b3, #008080)';<br />
    headerPlate.style.border = '2px solid #00cccc';<br />
    headerPlate.style.borderRadius = '1px';<br />
    headerPlate.style.padding = '8px 15px';<br />
    headerPlate.style.marginBottom = '15px';<br />
    headerPlate.style.textAlign = 'center';<br />
    headerPlate.style.position = 'relative';<br />
    headerPlate.style.boxShadow = 'inset 0 0 10px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.5)';<br />
    headerPlate.style.textShadow = '0 0 5px rgba(0, 255, 255, 0.7)';<br />
    <br />
    // Заголовок "ЦЕХ 9" в стиле ЧПУ дисплея<br />
    var header = document.createElement("div");<br />
    header.innerText = 'ЦЕХ 9';<br />
    header.style.color = '#00ffff';<br />
    header.style.fontSize = '16px';<br />
    header.style.fontWeight = 'bold';<br />
    header.style.textTransform = 'uppercase';<br />
    header.style.letterSpacing = '3px';<br />
    header.style.fontFamily = '"Share Tech Mono", "Courier New", monospace';<br />
    <br />
    headerPlate.appendChild(header);<br />
    mainContainer.appendChild(headerPlate);<br />
<br />
    // Контейнер для кнопок<br />
    var btnContainer = document.createElement("div");<br />
    btnContainer.style.display = 'flex';<br />
    btnContainer.style.flexDirection = 'column';<br />
    btnContainer.style.gap = '8px';<br />
    mainContainer.appendChild(btnContainer);<br />
<br />
    // Создаем кнопки видов<br />
    var buttons = [<br />
        { id: 'btnSView', text: 'ОБЩИЙ ВИД', onClick: ScriptSView },<br />
        { id: 'btnView1', text: 'ВИД 1', onClick: ScriptView1 },<br />
        { id: 'btnView2', text: 'ВИД 2', onClick: ScriptView2 },<br />
        { id: 'btnView3', text: 'ВИД 3', onClick: ScriptView3 },<br />
    ];<br />
<br />
    buttons.forEach(button =&gt; {<br />
        var btn = document.createElement("BUTTON");<br />
        btn.name = button.id;<br />
        btn.id = button.id;<br />
        btn.innerText = button.text;<br />
        styleCNCButton(btn);<br />
        btn.onclick = function () { button.onClick(btn); };<br />
        btnContainer.appendChild(btn);<br />
    });<br />
<br />
    // Кнопка INFO в стиле аварийной кнопки<br />
    var infoButton = document.createElement("BUTTON");<br />
    infoButton.name = 'btnInfo';<br />
    infoButton.id = 'btnInfo';<br />
    infoButton.innerText = 'INFO';<br />
    styleEmergencyButton(infoButton);<br />
    infoButton.onclick = function () { showInfoModal(); };<br />
    btnContainer.appendChild(infoButton);<br />
<br />
    // Нижняя техно-полоса<br />
    var footerStrip = document.createElement("div");<br />
    footerStrip.style.height = '3px';<br />
    footerStrip.style.background = 'linear-gradient(to right, #00cccc, #008080, #00cccc)';<br />
    footerStrip.style.marginTop = '15px';<br />
    footerStrip.style.borderRadius = '1px';<br />
    footerStrip.style.boxShadow = '0 0 5px rgba(0, 255, 255, 0.3)';<br />
    mainContainer.appendChild(footerStrip);<br />
<br />
    sceneView.appendChild(mainContainer);<br />
}<br />
<br />
// Функция для добавления техно-деталей ЧПУ<br />
function addCNCDetails(container) {<br />
    // Добавляем угловые элементы<br />
    const corners = [<br />
        { top: '0', left: '0', transform: 'none' },<br />
        { top: '0', right: '0', transform: 'scaleX(-1)' },<br />
        { bottom: '0', left: '0', transform: 'scaleY(-1)' },<br />
        { bottom: '0', right: '0', transform: 'scale(-1)' }<br />
    ];<br />
<br />
    corners.forEach(corner =&gt; {<br />
        var cornerElement = document.createElement("div");<br />
        cornerElement.style.position = 'absolute';<br />
        cornerElement.style.width = '15px';<br />
        cornerElement.style.height = '15px';<br />
        cornerElement.style.background = 'linear-gradient(135deg, #00cccc, #008080)';<br />
        cornerElement.style.border = '1px solid #00ffff';<br />
        cornerElement.style.boxShadow = '0 0 5px rgba(0, 255, 255, 0.4)';<br />
        cornerElement.style.transform = corner.transform;<br />
        Object.assign(cornerElement.style, corner);<br />
        container.appendChild(cornerElement);<br />
    });<br />
<br />
    // Добавляем техно-сетку на фон<br />
    var gridOverlay = document.createElement("div");<br />
    gridOverlay.style.position = 'absolute';<br />
    gridOverlay.style.top = '0';<br />
    gridOverlay.style.left = '0';<br />
    gridOverlay.style.width = '100%';<br />
    gridOverlay.style.height = '100%';<br />
    gridOverlay.style.backgroundImage = 'linear-gradient(to right, rgba(0, 204, 204, 0.1) 1px, transparent 1px), linear-gradient(to bottom, rgba(0, 204, 204, 0.1) 1px, transparent 1px)';<br />
    gridOverlay.style.backgroundSize = '10px 10px';<br />
    gridOverlay.style.pointerEvents = 'none';<br />
    gridOverlay.style.zIndex = '1';<br />
    container.appendChild(gridOverlay);<br />
}<br />
<br />
// Функция для стилизации обычных кнопок ЧПУ<br />
function styleCNCButton(btn) {<br />
    btn.style.fontSize = '12px';<br />
    btn.style.padding = '10px 15px';<br />
    btn.style.color = '#00ffff';<br />
    btn.style.background = 'linear-gradient(to bottom, #333, #222)';<br />
    btn.style.border = '2px solid #00cccc';<br />
    btn.style.borderRadius = '1px';<br />
    btn.style.cursor = 'pointer';<br />
    btn.style.transition = 'all 0.2s ease';<br />
    btn.style.fontWeight = 'bold';<br />
    btn.style.textTransform = 'uppercase';<br />
    btn.style.letterSpacing = '1px';<br />
    btn.style.textShadow = '0 0 3px rgba(0, 255, 255, 0.8)';<br />
    btn.style.fontFamily = '"Share Tech Mono", "Courier New", monospace';<br />
    btn.style.boxShadow = 'inset 0 0 5px rgba(0, 0, 0, 0.5), 0 2px 4px rgba(0, 0, 0, 0.3)';<br />
<br />
    // Эффект на hover<br />
    btn.onmouseenter = function () {<br />
        btn.style.background = 'linear-gradient(to bottom, #00cccc, #008080)';<br />
        btn.style.color = '#000';<br />
        btn.style.textShadow = '0 0 3px rgba(255, 255, 255, 0.8)';<br />
        btn.style.boxShadow = 'inset 0 0 10px rgba(0, 0, 0, 0.3), 0 0 15px rgba(0, 204, 204, 0.4)';<br />
    };<br />
    <br />
    btn.onmouseleave = function () {<br />
        btn.style.background = 'linear-gradient(to bottom, #333, #222)';<br />
        btn.style.color = '#00ffff';<br />
        btn.style.textShadow = '0 0 3px rgba(0, 255, 255, 0.8)';<br />
        btn.style.boxShadow = 'inset 0 0 5px rgba(0, 0, 0, 0.5), 0 2px 4px rgba(0, 0, 0, 0.3)';<br />
    };<br />
    <br />
    // Эффект при нажатии<br />
    btn.onmousedown = function () {<br />
        btn.style.transform = 'translateY(1px)';<br />
        btn.style.boxShadow = 'inset 0 0 8px rgba(0, 0, 0, 0.6), 0 1px 2px rgba(0, 0, 0, 0.3)';<br />
    };<br />
    <br />
    btn.onmouseup = function () {<br />
        btn.style.transform = 'translateY(0)';<br />
        btn.style.boxShadow = 'inset 0 0 5px rgba(0, 0, 0, 0.5), 0 2px 4px rgba(0, 0, 0, 0.3)';<br />
    };<br />
}<br />
<br />
// Функция для стилизации аварийной кнопки INFO<br />
function styleEmergencyButton(btn) {<br />
    btn.style.fontSize = '12px';<br />
    btn.style.padding = '10px 15px';<br />
    btn.style.color = '#fff';<br />
    btn.style.background = 'linear-gradient(to bottom, #ff4444, #cc0000)';<br />
    btn.style.border = '2px solid #ff6666';<br />
    btn.style.borderRadius = '1px';<br />
    btn.style.cursor = 'pointer';<br />
    btn.style.transition = 'all 0.2s ease';<br />
    btn.style.fontWeight = 'bold';<br />
    btn.style.textTransform = 'uppercase';<br />
    btn.style.letterSpacing = '1px';<br />
    btn.style.textShadow = '0 0 3px rgba(255, 0, 0, 0.8)';<br />
    btn.style.fontFamily = '"Share Tech Mono", "Courier New", monospace';<br />
    btn.style.boxShadow = 'inset 0 0 5px rgba(0, 0, 0, 0.5), 0 2px 4px rgba(0, 0, 0, 0.3)';<br />
    btn.style.marginTop = '10px';<br />
<br />
    // Эффект на hover<br />
    btn.onmouseenter = function () {<br />
        btn.style.background = 'linear-gradient(to bottom, #ff6666, #ff0000)';<br />
        btn.style.boxShadow = 'inset 0 0 10px rgba(0, 0, 0, 0.3), 0 0 15px rgba(255, 0, 0, 0.4)';<br />
        btn.style.transform = 'scale(1.05)';<br />
    };<br />
    <br />
    btn.onmouseleave = function () {<br />
        btn.style.background = 'linear-gradient(to bottom, #ff4444, #cc0000)';<br />
        btn.style.boxShadow = 'inset 0 0 5px rgba(0, 0, 0, 0.5), 0 2px 4px rgba(0, 0, 0, 0.3)';<br />
        btn.style.transform = 'scale(1)';<br />
    };<br />
    <br />
    // Эффект при нажатии<br />
    btn.onmousedown = function () {<br />
        btn.style.transform = 'scale(0.95)';<br />
        btn.style.boxShadow = 'inset 0 0 8px rgba(0, 0, 0, 0.6), 0 1px 2px rgba(0, 0, 0, 0.3)';<br />
    };<br />
    <br />
    btn.onmouseup = function () {<br />
        btn.style.transform = 'scale(1.05)';<br />
        btn.style.boxShadow = 'inset 0 0 10px rgba(0, 0, 0, 0.3), 0 0 15px rgba(255, 0, 0, 0.4)';<br />
    };<br />
}<br />
<br />
// Функция для показа модального окна в стиле ЧПУ<br />
function showInfoModal() {<br />
    // Создаем overlay<br />
    var overlay = document.createElement("div");<br />
    overlay.style.position = 'fixed';<br />
    overlay.style.top = '0';<br />
    overlay.style.left = '0';<br />
    overlay.style.width = '100%';<br />
    overlay.style.height = '100%';<br />
    overlay.style.background = 'rgba(0, 0, 0, 0.9)';<br />
    overlay.style.zIndex = '2000';<br />
    overlay.style.display = 'flex';<br />
    overlay.style.justifyContent = 'center';<br />
    overlay.style.alignItems = 'center';<br />
    overlay.style.backdropFilter = 'blur(3px)';<br />
    <br />
    // Создаем модальное окно в стиле ЧПУ дисплея<br />
    var modal = document.createElement("div");<br />
    modal.style.background = 'linear-gradient(145deg, #1a1a1a, #2a2a2a)';<br />
    modal.style.border = '3px solid #00cccc';<br />
    modal.style.borderRadius = '2px';<br />
    modal.style.padding = '25px';<br />
    modal.style.maxWidth = '500px';<br />
    modal.style.width = '80%';<br />
    modal.style.boxShadow = '0 0 30px rgba(0, 204, 204, 0.4), inset 0 0 20px rgba(0, 0, 0, 0.6)';<br />
    modal.style.position = 'relative';<br />
    modal.style.color = '#00ffff';<br />
    modal.style.fontFamily = '"Share Tech Mono", "Courier New", monospace';<br />
    modal.style.textShadow = '0 0 3px rgba(0, 255, 255, 0.7)';<br />
<br />
    // Добавляем техно-детали к модальному окну<br />
    addCNCDetails(modal);<br />
<br />
    // Заголовок модального окна<br />
    var title = document.createElement("h2");<br />
    title.textContent = 'СИСТЕМНАЯ ИНФОРМАЦИЯ';<br />
    title.style.color = '#00ff00';<br />
    title.style.marginBottom = '20px';<br />
    title.style.textAlign = 'center';<br />
    title.style.fontSize = '18px';<br />
    title.style.fontWeight = 'bold';<br />
    title.style.textTransform = 'uppercase';<br />
    title.style.letterSpacing = '2px';<br />
    modal.appendChild(title);<br />
    <br />
    // Текст информации<br />
    var infoText = document.createElement("div");<br />
    infoText.innerHTML = <br />
        '&lt;p style="margin-bottom: 12px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;3D-СЦЕНА:&lt;/strong&gt; ЦЕХ 9' +<br />
        '&lt;/p&gt;' +<br />
        '&lt;p style="margin-bottom: 12px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;РАЗРАБОТЧИК:&lt;/strong&gt; ПОНЯТОВ НИКИТА' +<br />
        '&lt;/p&gt;' +<br />
        '&lt;p style="margin-bottom: 15px; line-height: 1.5; font-size: 14px; color: #00cccc;"&gt;' +<br />
            '&lt;strong style="color: #00ff00;"&gt;СТАТУС:&lt;/strong&gt; СИСТЕМА АКТИВНА' +<br />
        '&lt;/p&gt;';<br />
    modal.appendChild(infoText);<br />
    <br />
    // Контейнер для контактов<br />
    var contactContainer = document.createElement("div");<br />
    contactContainer.style.textAlign = 'center';<br />
    contactContainer.style.marginBottom = '20px';<br />
    contactContainer.style.padding = '12px';<br />
    contactContainer.style.background = 'linear-gradient(145deg, #0d2b2b, #1a4040)';<br />
    contactContainer.style.border = '1px solid #00cccc';<br />
    contactContainer.style.borderRadius = '1px';<br />
    contactContainer.style.boxShadow = 'inset 0 0 10px rgba(0, 0, 0, 0.4)';<br />
    <br />
    var contactTitle = document.createElement("div");<br />
    contactTitle.textContent = 'ТЕХНИЧЕСКАЯ ПОДДЕРЖКА';<br />
    contactTitle.style.color = '#00ff00';<br />
    contactTitle.style.marginBottom = '8px';<br />
    contactTitle.style.fontSize = '12px';<br />
    contactTitle.style.fontWeight = 'bold';<br />
    contactContainer.appendChild(contactTitle);<br />
    <br />
    // Ссылка на email<br />
    var emailLink = document.createElement("a");<br />
    emailLink.href = 'mailto:support@winnum.ru';<br />
    emailLink.textContent = 'support@winnum.ru';<br />
    emailLink.style.color = '#00ffff';<br />
    emailLink.style.textDecoration = 'none';<br />
    emailLink.style.fontWeight = 'bold';<br />
    emailLink.style.fontSize = '14px';<br />
    emailLink.style.padding = '6px 12px';<br />
    emailLink.style.display = 'inline-block';<br />
    emailLink.style.transition = 'all 0.3s ease';<br />
    emailLink.style.border = '1px solid #00cccc';<br />
    emailLink.style.background = 'rgba(0, 204, 204, 0.1)';<br />
    <br />
    emailLink.onmouseenter = function() {<br />
        emailLink.style.background = 'rgba(0, 204, 204, 0.3)';<br />
        emailLink.style.color = '#00ff00';<br />
        emailLink.style.boxShadow = '0 0 10px rgba(0, 204, 204, 0.4)';<br />
    };<br />
    <br />
    emailLink.onmouseleave = function() {<br />
        emailLink.style.background = 'rgba(0, 204, 204, 0.1)';<br />
        emailLink.style.color = '#00ffff';<br />
        emailLink.style.boxShadow = 'none';<br />
    };<br />
    <br />
    contactContainer.appendChild(emailLink);<br />
    modal.appendChild(contactContainer);<br />
    <br />
    // Кнопка закрытия<br />
    var closeButton = document.createElement("button");<br />
    closeButton.textContent = 'ЗАКРЫТЬ [ESC]';<br />
    closeButton.style.background = 'linear-gradient(to bottom, #333, #222)';<br />
    closeButton.style.color = '#00cccc';<br />
    closeButton.style.border = '2px solid #00cccc';<br />
    closeButton.style.padding = '8px 20px';<br />
    closeButton.style.borderRadius = '1px';<br />
    closeButton.style.cursor = 'pointer';<br />
    closeButton.style.fontWeight = 'bold';<br />
    closeButton.style.margin = '0 auto';<br />
    closeButton.style.display = 'block';<br />
    closeButton.style.transition = 'all 0.3s ease';<br />
    closeButton.style.fontFamily = '"Share Tech Mono", "Courier New", monospace';<br />
    closeButton.style.textTransform = 'uppercase';<br />
    <br />
    closeButton.onmouseenter = function() {<br />
        closeButton.style.background = 'linear-gradient(to bottom, #00cccc, #008080)';<br />
        closeButton.style.color = '#000';<br />
    };<br />
    <br />
    closeButton.onmouseleave = function() {<br />
        closeButton.style.background = 'linear-gradient(to bottom, #333, #222)';<br />
        closeButton.style.color = '#00cccc';<br />
    };<br />
    <br />
    closeButton.onclick = function() {<br />
        document.body.removeChild(overlay);<br />
    };<br />
    <br />
    modal.appendChild(closeButton);<br />
    overlay.appendChild(modal);<br />
    <br />
    // Добавляем overlay на страницу<br />
    document.body.appendChild(overlay);<br />
    <br />
    // Закрытие по клику на overlay<br />
    overlay.onclick = function(e) {<br />
        if (e.target === overlay) {<br />
            document.body.removeChild(overlay);<br />
        }<br />
    };<br />
    <br />
    // Закрытие по ESC<br />
    document.addEventListener('keydown', function closeModal(e) {<br />
        if (e.key === 'Escape') {<br />
            document.body.removeChild(overlay);<br />
            document.removeEventListener('keydown', closeModal);<br />
        }<br />
    });<br />
}<br />
<br />
// Логика для общего вида<br />
function ScriptSView(cButton) {<br />
    console.log('Общий вид активирован');<br />
    camera.position.x = 1.76;<br />
    camera.position.y = 49.84;<br />
    camera.position.z = 39;<br />
    camera.rotation.x = -52.42 * Math.PI / 180;<br />
    camera.rotation.y = 0 * Math.PI / 180;<br />
    camera.rotation.z = 0 * Math.PI / 180;<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
}<br />
<br />
// Логика для вида 1<br />
function ScriptView1(cButton) {<br />
    console.log('Вид 1 активирован');<br />
    camera.position.x = -41;<br />
    camera.position.y = 32.43;<br />
    camera.position.z = 3.4;<br />
    camera.rotation.x = -58 * Math.PI / 180;<br />
    camera.rotation.y = -37.42 * Math.PI / 180;<br />
    camera.rotation.z = -44.3 * Math.PI / 180;<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
}<br />
<br />
// Логика для вида 2<br />
function ScriptView2(cButton) {<br />
    console.log('Вид 2 активирован');<br />
    camera.position.x = 27.72;<br />
    camera.position.y = 40.57;<br />
    camera.position.z = 37.53;<br />
    camera.rotation.x = -47.26 * Math.PI / 180;<br />
    camera.rotation.y = 9.58 * Math.PI / 180;<br />
    camera.rotation.z = 10.21 * Math.PI / 180;<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
}<br />
<br />
// Логика для вида 3<br />
function ScriptView3(cButton) {<br />
    console.log('Вид 3 активирован');<br />
    camera.position.x = -53.74;<br />
    camera.position.y = 28.2;<br />
    camera.position.z = 30.92;<br />
    camera.rotation.x = -44.4 * Math.PI / 180;<br />
    camera.rotation.y = -43.24 * Math.PI / 180;<br />
    camera.rotation.z = -33.86 * Math.PI / 180;<br />
    camera.updateProjectionMatrix();<br />
    camera.updateMatrix();<br />
}</code></div></div><br /><!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://community.winnum.io/images/attachtypes/zip.png" title="ZIP File" border="0" alt=".zip" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=59" target="_blank" title="">ViewButtons (2).zip</a> (Размер: 18.8 KB / Загрузок: 1)
<!-- end: postbit_attachments_attachment -->]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Добавление библиотеки в приложение]]></title>
			<link>https://community.winnum.io/showthread.php?tid=36</link>
			<pubDate>Fri, 22 Aug 2025 10:18:24 +0300</pubDate>
			<dc:creator><![CDATA[<a href="https://community.winnum.io/member.php?action=profile&uid=8">Lamantur</a>]]></dc:creator>
			<guid isPermaLink="false">https://community.winnum.io/showthread.php?tid=36</guid>
			<description><![CDATA[Добрый день коллеги. <br />
<br />
Сегодня хочу рассказать вам одну интересную тему;<br />
Редактор динамических приложений уже содержит в себе множество библиотек для JavaScript, библиотеки это подготовленный заранее набор функций, который позволяет сократить код, сосредоточившись на выполняемой задаче. Вам не нужно самостоятельно отрисовывать график в формате svg, за вас это может сделать встроенный chart.js, d3.js, c3.js. <br />
<br />
Тем не менее нельзя предусмотреть заранее все задачи, которые могут решаться с помощью динамических приложений, поэтому я расскажу как действовать, если нужная библиотека или дополнение к ней не нашлись в списке библиотек редактора.<br />
<br />
Для начала попробуем загрузить библиотеки online, т.е. предполагается, что пользователь может пользоваться интернетом, библиотеки в этом случае загружаются через ссылку.<br />
<br />
1. Добавление через HTML<br />
Любой контейнер для этого подойдет, он загружается асинхронно и перейдет по ссылке, инициализировав библиотеку<br />
&lt;script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"&gt;&lt;/script&gt;<br />
Это самый простой и работающий способ, главное дождаться загрузки элемента перед использованием библиотеки. <br />
<span style="font-style: italic;" class="mycode_i">Эта ссылка дана для примера JQuery уже установлен в редакторе, скачивать его повторно не нужно.</span><br />
<br />
2. Добавление через JavaScript<br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>// Создаем элемент script<br />
const script = document.createElement('script');<br />
script.src = 'https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js';<br />
<br />
// Добавляем на страницу<br />
document.head.appendChild(script);<br />
<br />
// Обработка загрузки<br />
script.onload = function() {<br />
    console.log('Библиотека загружена!');<br />
    // Здесь можно использовать библиотеку<br />
    // &#36;(document).ready(function() { ... });<br />
};<br />
<br />
script.onerror = function() {<br />
    console.error('Ошибка загрузки библиотеки');<br />
};</code></div></div><br />
Асинхронная загрузка с Promise :<br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>function loadScript(src) {<br />
    return new Promise((resolve, reject) =&gt; {<br />
        const script = document.createElement('script');<br />
        script.src = src;<br />
        script.async = true;<br />
        <br />
        script.onload = () =&gt; resolve(script);<br />
        script.onerror = () =&gt; reject(new Error(`Ошибка загрузки: &#36;{src}`));<br />
        <br />
        document.head.appendChild(script);<br />
    });<br />
}<br />
<br />
// Использование<br />
loadScript('https://cdn.jsdelivivr.net/npm/lodash@4.17.21/lodash.min.js')<br />
    .then(() =&gt; {<br />
        console.log('Библиотека загружена');<br />
        // Используем библиотеку<br />
    })<br />
    .catch(error =&gt; console.error(error));</code></div></div><br />
Добавление CSS библиотек<br />
&lt;link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"&gt;<br />
или<br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>const link = document.createElement('link');<br />
link.rel = 'stylesheet';<br />
link.href = 'https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css';<br />
document.head.appendChild(link);</code></div></div><br />
<span style="font-style: italic;" class="mycode_i">bootstrap тоже уже установлен, делать это повторно не нужно</span><br />
<br />
<br />
<br />
Вдруг, выясняется, что у заказчика изолированная сеть и все эти ссылки не работают. Как быть?<br />
Почти все библиотеки можно скачать себе в медиа библиотеку и использовать как обычные JS или css<br />
Перейдите по ссылке самостоятельно и библиотека откроется в браузере в виде сокращенного JavaScript кода.<br />
Библиотеки специально хранятся в сжатом виде без правил оформления и комментариев, часто используются однобуквенные переменные.<br />
<br />
Весь текст скопируйте в файл с расширением .js для JavaScript и .css для стилей.<br />
<br />
Загрузите файлы в медиа библиотеку и используйте ссылки на эти файлы вместо ссылок cdn.<br />
В медиа библиотеке предусмотрена отдельная кнопка "скопировать ссылку". Сама ссылка выглядит так:<br />
<br />
/Winnum/resources/themes/current/images/app/ui/designer/media/38/createChart.js<br />
<br />
Для того чтобы избежать проблем при импорте вашего приложения на другие платформы не стоит использовать прямые ссылки на папки с файлами, они могут измениться.<br />
Ранее мы уже делали это, когда инициализировали наш manifest.js. Повторим еще раз.<br />
<br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>&#36;(window).ready(function(){<br />
    loadResources(true);<br />
});<br />
function loadResources(initial){<br />
    var oid = '';<br />
    var appid = '';<br />
    baseSdkUtils.the = {};<br />
    baseSdkUtils.service.WNFactory.getPersistable(baseApplicationDesignerUtils.getAppid(), function(data){<br />
        if ( !baseSdkUtils.isSuccess(data) ){<br />
            alert('Error: ' + baseSdkUtils.decode(data.innerHTML));<br />
            console.warn(baseSdkUtils.decode(data.innerHTML));<br />
            return;<br />
        }<br />
        /******* Случай, когда код выполняется в приложении ******/<br />
        if ( data.getElementsByTagName('item').length &gt; 0 ){<br />
            oid = data.getElementsByTagName('item')[0].getAttribute('ApplicationInfo__classNameA15');<br />
            oid += ':';<br />
            appid = data.getElementsByTagName('item')[0].getAttribute('ApplicationInfo__idA15');<br />
            oid += appid;<br />
        }<br />
        /****** Случай, когда код выполняется в редакторе ********/<br />
        else{<br />
            oid = baseApplicationDesignerUtils.oid;<br />
            appid = oid.split(':')[1];<br />
        }<br />
        /****** Создание структуры для хранения атрибутов ********/<br />
            baseSdkUtils.the.appoid = baseApplicationDesignerUtils.getAppid();<br />
            baseSdkUtils.the.appid = appid;<br />
            baseSdkUtils.the.oid = oid;<br />
            baseSdkUtils.the.media_url = '/' + baseSdkUtils.appId + '/resources/themes/current/images/app/ui/designer/media/' + appid + '/';        <br />
            <br />
            &#36;('#menu').load(baseSdkUtils.the.media_url + '/menu.html');<br />
        /**** Загрузка js ****************************************/<br />
            jQuery.ajax({<br />
                url: baseSdkUtils.the.media_url + ' createChart.js',<br />
                dataType: 'script',<br />
                success: function(){ loadResourcesProcessor(initial); },<br />
                async: true<br />
            });<br />
    });<br />
}</code></div></div>Вы можете не делать все это повторно, у вас уже есть переменная  baseSdkUtils.the.media_url, которая содержит весь путь до библиотеки, остается указать файл, если он в отдельной папке, это тоже нужно добавить к названию файла.<br />
Помимо описанных способов вы можете так же использовать  ajax, как и в примере, если запускать что либо из библиотеки сразу не нужно, уберите поле success, асинхронный вызов в данном случае тоже не обязателен, если библиотека инициализируется один раз.<br />
<br />
Будьте осторожны, некоторые библиотеки влияют на скорость открытия страницы, поищите, возможно есть сокращенная версия библиотеки, которой вам будет достаточно. <br />
<br />
Создавайте свои библиотеки с набором нужных функций и стилей, чтобы использовать их в дальнейших разработках.<br />
Вы можете создать заготовку html элемента неограниченного размера. Добавьте туда все стили и скрипты, которые участвуют непосредственно в элементе и работают локально и переносите ее в новые приложения, вам больше не понадобится создавать весь интерфейс заново:<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://community.winnum.io/images/attachtypes/image.png" title="PNG Image" border="0" alt=".png" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=58" target="_blank" title="">html_conteiner.png</a> (Размер: 15.75 KB / Загрузок: 22)
<!-- end: postbit_attachments_attachment -->]]></description>
			<content:encoded><![CDATA[Добрый день коллеги. <br />
<br />
Сегодня хочу рассказать вам одну интересную тему;<br />
Редактор динамических приложений уже содержит в себе множество библиотек для JavaScript, библиотеки это подготовленный заранее набор функций, который позволяет сократить код, сосредоточившись на выполняемой задаче. Вам не нужно самостоятельно отрисовывать график в формате svg, за вас это может сделать встроенный chart.js, d3.js, c3.js. <br />
<br />
Тем не менее нельзя предусмотреть заранее все задачи, которые могут решаться с помощью динамических приложений, поэтому я расскажу как действовать, если нужная библиотека или дополнение к ней не нашлись в списке библиотек редактора.<br />
<br />
Для начала попробуем загрузить библиотеки online, т.е. предполагается, что пользователь может пользоваться интернетом, библиотеки в этом случае загружаются через ссылку.<br />
<br />
1. Добавление через HTML<br />
Любой контейнер для этого подойдет, он загружается асинхронно и перейдет по ссылке, инициализировав библиотеку<br />
&lt;script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"&gt;&lt;/script&gt;<br />
Это самый простой и работающий способ, главное дождаться загрузки элемента перед использованием библиотеки. <br />
<span style="font-style: italic;" class="mycode_i">Эта ссылка дана для примера JQuery уже установлен в редакторе, скачивать его повторно не нужно.</span><br />
<br />
2. Добавление через JavaScript<br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>// Создаем элемент script<br />
const script = document.createElement('script');<br />
script.src = 'https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js';<br />
<br />
// Добавляем на страницу<br />
document.head.appendChild(script);<br />
<br />
// Обработка загрузки<br />
script.onload = function() {<br />
    console.log('Библиотека загружена!');<br />
    // Здесь можно использовать библиотеку<br />
    // &#36;(document).ready(function() { ... });<br />
};<br />
<br />
script.onerror = function() {<br />
    console.error('Ошибка загрузки библиотеки');<br />
};</code></div></div><br />
Асинхронная загрузка с Promise :<br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>function loadScript(src) {<br />
    return new Promise((resolve, reject) =&gt; {<br />
        const script = document.createElement('script');<br />
        script.src = src;<br />
        script.async = true;<br />
        <br />
        script.onload = () =&gt; resolve(script);<br />
        script.onerror = () =&gt; reject(new Error(`Ошибка загрузки: &#36;{src}`));<br />
        <br />
        document.head.appendChild(script);<br />
    });<br />
}<br />
<br />
// Использование<br />
loadScript('https://cdn.jsdelivivr.net/npm/lodash@4.17.21/lodash.min.js')<br />
    .then(() =&gt; {<br />
        console.log('Библиотека загружена');<br />
        // Используем библиотеку<br />
    })<br />
    .catch(error =&gt; console.error(error));</code></div></div><br />
Добавление CSS библиотек<br />
&lt;link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"&gt;<br />
или<br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>const link = document.createElement('link');<br />
link.rel = 'stylesheet';<br />
link.href = 'https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css';<br />
document.head.appendChild(link);</code></div></div><br />
<span style="font-style: italic;" class="mycode_i">bootstrap тоже уже установлен, делать это повторно не нужно</span><br />
<br />
<br />
<br />
Вдруг, выясняется, что у заказчика изолированная сеть и все эти ссылки не работают. Как быть?<br />
Почти все библиотеки можно скачать себе в медиа библиотеку и использовать как обычные JS или css<br />
Перейдите по ссылке самостоятельно и библиотека откроется в браузере в виде сокращенного JavaScript кода.<br />
Библиотеки специально хранятся в сжатом виде без правил оформления и комментариев, часто используются однобуквенные переменные.<br />
<br />
Весь текст скопируйте в файл с расширением .js для JavaScript и .css для стилей.<br />
<br />
Загрузите файлы в медиа библиотеку и используйте ссылки на эти файлы вместо ссылок cdn.<br />
В медиа библиотеке предусмотрена отдельная кнопка "скопировать ссылку". Сама ссылка выглядит так:<br />
<br />
/Winnum/resources/themes/current/images/app/ui/designer/media/38/createChart.js<br />
<br />
Для того чтобы избежать проблем при импорте вашего приложения на другие платформы не стоит использовать прямые ссылки на папки с файлами, они могут измениться.<br />
Ранее мы уже делали это, когда инициализировали наш manifest.js. Повторим еще раз.<br />
<br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>&#36;(window).ready(function(){<br />
    loadResources(true);<br />
});<br />
function loadResources(initial){<br />
    var oid = '';<br />
    var appid = '';<br />
    baseSdkUtils.the = {};<br />
    baseSdkUtils.service.WNFactory.getPersistable(baseApplicationDesignerUtils.getAppid(), function(data){<br />
        if ( !baseSdkUtils.isSuccess(data) ){<br />
            alert('Error: ' + baseSdkUtils.decode(data.innerHTML));<br />
            console.warn(baseSdkUtils.decode(data.innerHTML));<br />
            return;<br />
        }<br />
        /******* Случай, когда код выполняется в приложении ******/<br />
        if ( data.getElementsByTagName('item').length &gt; 0 ){<br />
            oid = data.getElementsByTagName('item')[0].getAttribute('ApplicationInfo__classNameA15');<br />
            oid += ':';<br />
            appid = data.getElementsByTagName('item')[0].getAttribute('ApplicationInfo__idA15');<br />
            oid += appid;<br />
        }<br />
        /****** Случай, когда код выполняется в редакторе ********/<br />
        else{<br />
            oid = baseApplicationDesignerUtils.oid;<br />
            appid = oid.split(':')[1];<br />
        }<br />
        /****** Создание структуры для хранения атрибутов ********/<br />
            baseSdkUtils.the.appoid = baseApplicationDesignerUtils.getAppid();<br />
            baseSdkUtils.the.appid = appid;<br />
            baseSdkUtils.the.oid = oid;<br />
            baseSdkUtils.the.media_url = '/' + baseSdkUtils.appId + '/resources/themes/current/images/app/ui/designer/media/' + appid + '/';        <br />
            <br />
            &#36;('#menu').load(baseSdkUtils.the.media_url + '/menu.html');<br />
        /**** Загрузка js ****************************************/<br />
            jQuery.ajax({<br />
                url: baseSdkUtils.the.media_url + ' createChart.js',<br />
                dataType: 'script',<br />
                success: function(){ loadResourcesProcessor(initial); },<br />
                async: true<br />
            });<br />
    });<br />
}</code></div></div>Вы можете не делать все это повторно, у вас уже есть переменная  baseSdkUtils.the.media_url, которая содержит весь путь до библиотеки, остается указать файл, если он в отдельной папке, это тоже нужно добавить к названию файла.<br />
Помимо описанных способов вы можете так же использовать  ajax, как и в примере, если запускать что либо из библиотеки сразу не нужно, уберите поле success, асинхронный вызов в данном случае тоже не обязателен, если библиотека инициализируется один раз.<br />
<br />
Будьте осторожны, некоторые библиотеки влияют на скорость открытия страницы, поищите, возможно есть сокращенная версия библиотеки, которой вам будет достаточно. <br />
<br />
Создавайте свои библиотеки с набором нужных функций и стилей, чтобы использовать их в дальнейших разработках.<br />
Вы можете создать заготовку html элемента неограниченного размера. Добавьте туда все стили и скрипты, которые участвуют непосредственно в элементе и работают локально и переносите ее в новые приложения, вам больше не понадобится создавать весь интерфейс заново:<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://community.winnum.io/images/attachtypes/image.png" title="PNG Image" border="0" alt=".png" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=58" target="_blank" title="">html_conteiner.png</a> (Размер: 15.75 KB / Загрузок: 22)
<!-- end: postbit_attachments_attachment -->]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[WinnumAjaxAuthHandler - Пример]]></title>
			<link>https://community.winnum.io/showthread.php?tid=35</link>
			<pubDate>Tue, 27 May 2025 12:15:59 +0300</pubDate>
			<dc:creator><![CDATA[<a href="https://community.winnum.io/member.php?action=profile&uid=8">Lamantur</a>]]></dc:creator>
			<guid isPermaLink="false">https://community.winnum.io/showthread.php?tid=35</guid>
			<description><![CDATA[Всем привет!<br />
<br />
Для начала, хотел бы предупредить, что хотя ниже и упоминается конкретно модуль Испытания, но этот "Лайфхак" пригодится в любом другом месте. Поэтому смело окунайтесь в пост.<br />
<br />
WINNUM SDK предлагает довольно обширное количество различных методов для разработки приложений. Однако, я недавно натолкнулся на некоторую проблему. В модуле Winnum Испытания мне хотелось получить информацию по испытаниям и вообще пользоваться стандартными отчетами у себя. Испытания предлагают два варианта нахождения прошедших испытаний:<br />
1. Отчет об испытаниях;<br />
2. Отчет по оборудованию;<br />
Второй вариант легко повторить у себя в приложении, для этого не обязательно получать весь отчет. В поле ввода вводится стенд, а так же время, по которому и производится поиск. Не очень трудно догадаться, что выполнив функцию getSignal и указав конкретный сигнал, а можно и сразу несколько - мы получим все данные по прошедшим испытаниям, и даже больше чем в отчете, если нам это надо.<br />
Сложность возникает, когда мы смотрим на Отчет об испытаниях.<br />
<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://community.winnum.io/images/attachtypes/image.png" title="PNG Image" border="0" alt=".png" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=53" target="_blank" title="">Отчет об испытаниях.png</a> (Размер: 89.54 KB / Загрузок: 48)
<!-- end: postbit_attachments_attachment --><br />
<img src="https://community.winnum.io/images/smilies/huh.png" alt="Huh" title="Huh" class="smilie smilie_17" /> <img src="https://community.winnum.io/images/smilies/huh.png" alt="Huh" title="Huh" class="smilie smilie_17" /> <img src="https://community.winnum.io/images/smilies/huh.png" alt="Huh" title="Huh" class="smilie smilie_17" /> <br />
<br />
Отчет отрабатывает довольно быстро а данные из этой таблицы были бы очень полезны в моем приложении.  <img src="https://community.winnum.io/images/smilies/angel.png" alt="Angel" title="Angel" class="smilie smilie_10" /><br />
Я хотел найти путь, по которому смог бы что-то получить из этой таблицы, но решение оказалось простым и изящным.<br />
Далее просто описываю путь, повторяйте за мной  <img src="https://community.winnum.io/images/smilies/cool.png" alt="Cool" title="Cool" class="smilie smilie_3" /><br />
<br />
Нажимаем F12<br />
<br />
Переходим на вкладку Сеть<br />
<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://community.winnum.io/images/attachtypes/image.png" title="PNG Image" border="0" alt=".png" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=54" target="_blank" title="">Отчет об испытаниях 2.png</a> (Размер: 113.64 KB / Загрузок: 49)
<!-- end: postbit_attachments_attachment --><br />
<br />
Далее (выражаясь общими словами) выполняем запрос:<br />
<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://community.winnum.io/images/attachtypes/image.png" title="PNG Image" border="0" alt=".png" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=55" target="_blank" title="">Отчет об испытаниях 3.png</a> (Размер: 162.89 KB / Загрузок: 50)
<!-- end: postbit_attachments_attachment --><br />
<br />
<br />
Далее нажимаю два раза на запрос в WinnumAjaxAuthHandler и вижу:<br />
<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://community.winnum.io/images/attachtypes/image.png" title="PNG Image" border="0" alt=".png" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=56" target="_blank" title="">Отчет об испытаниях 4.png</a> (Размер: 368.24 KB / Загрузок: 49)
<!-- end: postbit_attachments_attachment --><br />
<br />
<img src="https://community.winnum.io/images/smilies/biggrin.png" alt="Big Grin" title="Big Grin" class="smilie smilie_4" /> <br />
<br />
<br />
т.е. если в браузере отправить такой запрос, то я сразу получу все данные для этой таблицы. И даже ссылки и картинки присутствуют в ответе.<br />
<br />
Осталось дело за малым - научиться работать с Ajax:<br />
<br />
Делаем кнопку и в  нее - событие:<br />
<br />
<br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>&#36;('#FC204DC3-2522-438C-B523-09B4C85ABEBF').click(function(event){<br />
const baseUrl = 'http://127.0.0.1/Winnum/servlets/WinnumAjaxAuthHandler';<br />
const params = {<br />
  cmdclass: 'winnum.views.pages.app.tts.reports.TestingSearchHandler',<br />
  cmdmethod: 'performSearch',<br />
  appid: 'winnum.org.app.WNApplicationInstance:1',<br />
  callerId: 'refreshLoadingList',<br />
  barCode: '',<br />
  testingNumber: '',<br />
  year: '2025',<br />
  number: '',<br />
  mode: 'yes',<br />
  order: 'asc',<br />
  offset: '0',<br />
  limit: '100',<br />
  _: ''<br />
};<br />
<br />
const url = `&#36;{baseUrl}?&#36;{new URLSearchParams(params).toString()}`;<br />
<br />
fetch(url)<br />
  .then(response =&gt; {<br />
    if (!response.ok) {<br />
      throw new Error('Network response was not ok');<br />
    }<br />
    return response.json(); // или response.text() если данные не в JSON<br />
  })<br />
  .then(data =&gt; {<br />
    console.log('Полученные данные:', data);<br />
    // Здесь вы можете работать с полученными данными<br />
  })<br />
  .catch(error =&gt; {<br />
    console.error('Произошла ошибка:', error);<br />
  });<br />
});</code></div></div><br />
<br />
Запускаем, нажимаем:<br />
<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://community.winnum.io/images/attachtypes/image.png" title="PNG Image" border="0" alt=".png" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=57" target="_blank" title="">Отчет об испытаниях 5.png</a> (Размер: 123.04 KB / Загрузок: 49)
<!-- end: postbit_attachments_attachment --><br />
<br />
<br />
Теперь у вас есть данные и вы можете соорудить таблицу, графики, мини-отчеты, использовать дальше для любых своих целей. <img src="https://community.winnum.io/images/smilies/cool.png" alt="Cool" title="Cool" class="smilie smilie_3" />]]></description>
			<content:encoded><![CDATA[Всем привет!<br />
<br />
Для начала, хотел бы предупредить, что хотя ниже и упоминается конкретно модуль Испытания, но этот "Лайфхак" пригодится в любом другом месте. Поэтому смело окунайтесь в пост.<br />
<br />
WINNUM SDK предлагает довольно обширное количество различных методов для разработки приложений. Однако, я недавно натолкнулся на некоторую проблему. В модуле Winnum Испытания мне хотелось получить информацию по испытаниям и вообще пользоваться стандартными отчетами у себя. Испытания предлагают два варианта нахождения прошедших испытаний:<br />
1. Отчет об испытаниях;<br />
2. Отчет по оборудованию;<br />
Второй вариант легко повторить у себя в приложении, для этого не обязательно получать весь отчет. В поле ввода вводится стенд, а так же время, по которому и производится поиск. Не очень трудно догадаться, что выполнив функцию getSignal и указав конкретный сигнал, а можно и сразу несколько - мы получим все данные по прошедшим испытаниям, и даже больше чем в отчете, если нам это надо.<br />
Сложность возникает, когда мы смотрим на Отчет об испытаниях.<br />
<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://community.winnum.io/images/attachtypes/image.png" title="PNG Image" border="0" alt=".png" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=53" target="_blank" title="">Отчет об испытаниях.png</a> (Размер: 89.54 KB / Загрузок: 48)
<!-- end: postbit_attachments_attachment --><br />
<img src="https://community.winnum.io/images/smilies/huh.png" alt="Huh" title="Huh" class="smilie smilie_17" /> <img src="https://community.winnum.io/images/smilies/huh.png" alt="Huh" title="Huh" class="smilie smilie_17" /> <img src="https://community.winnum.io/images/smilies/huh.png" alt="Huh" title="Huh" class="smilie smilie_17" /> <br />
<br />
Отчет отрабатывает довольно быстро а данные из этой таблицы были бы очень полезны в моем приложении.  <img src="https://community.winnum.io/images/smilies/angel.png" alt="Angel" title="Angel" class="smilie smilie_10" /><br />
Я хотел найти путь, по которому смог бы что-то получить из этой таблицы, но решение оказалось простым и изящным.<br />
Далее просто описываю путь, повторяйте за мной  <img src="https://community.winnum.io/images/smilies/cool.png" alt="Cool" title="Cool" class="smilie smilie_3" /><br />
<br />
Нажимаем F12<br />
<br />
Переходим на вкладку Сеть<br />
<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://community.winnum.io/images/attachtypes/image.png" title="PNG Image" border="0" alt=".png" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=54" target="_blank" title="">Отчет об испытаниях 2.png</a> (Размер: 113.64 KB / Загрузок: 49)
<!-- end: postbit_attachments_attachment --><br />
<br />
Далее (выражаясь общими словами) выполняем запрос:<br />
<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://community.winnum.io/images/attachtypes/image.png" title="PNG Image" border="0" alt=".png" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=55" target="_blank" title="">Отчет об испытаниях 3.png</a> (Размер: 162.89 KB / Загрузок: 50)
<!-- end: postbit_attachments_attachment --><br />
<br />
<br />
Далее нажимаю два раза на запрос в WinnumAjaxAuthHandler и вижу:<br />
<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://community.winnum.io/images/attachtypes/image.png" title="PNG Image" border="0" alt=".png" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=56" target="_blank" title="">Отчет об испытаниях 4.png</a> (Размер: 368.24 KB / Загрузок: 49)
<!-- end: postbit_attachments_attachment --><br />
<br />
<img src="https://community.winnum.io/images/smilies/biggrin.png" alt="Big Grin" title="Big Grin" class="smilie smilie_4" /> <br />
<br />
<br />
т.е. если в браузере отправить такой запрос, то я сразу получу все данные для этой таблицы. И даже ссылки и картинки присутствуют в ответе.<br />
<br />
Осталось дело за малым - научиться работать с Ajax:<br />
<br />
Делаем кнопку и в  нее - событие:<br />
<br />
<br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>&#36;('#FC204DC3-2522-438C-B523-09B4C85ABEBF').click(function(event){<br />
const baseUrl = 'http://127.0.0.1/Winnum/servlets/WinnumAjaxAuthHandler';<br />
const params = {<br />
  cmdclass: 'winnum.views.pages.app.tts.reports.TestingSearchHandler',<br />
  cmdmethod: 'performSearch',<br />
  appid: 'winnum.org.app.WNApplicationInstance:1',<br />
  callerId: 'refreshLoadingList',<br />
  barCode: '',<br />
  testingNumber: '',<br />
  year: '2025',<br />
  number: '',<br />
  mode: 'yes',<br />
  order: 'asc',<br />
  offset: '0',<br />
  limit: '100',<br />
  _: ''<br />
};<br />
<br />
const url = `&#36;{baseUrl}?&#36;{new URLSearchParams(params).toString()}`;<br />
<br />
fetch(url)<br />
  .then(response =&gt; {<br />
    if (!response.ok) {<br />
      throw new Error('Network response was not ok');<br />
    }<br />
    return response.json(); // или response.text() если данные не в JSON<br />
  })<br />
  .then(data =&gt; {<br />
    console.log('Полученные данные:', data);<br />
    // Здесь вы можете работать с полученными данными<br />
  })<br />
  .catch(error =&gt; {<br />
    console.error('Произошла ошибка:', error);<br />
  });<br />
});</code></div></div><br />
<br />
Запускаем, нажимаем:<br />
<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://community.winnum.io/images/attachtypes/image.png" title="PNG Image" border="0" alt=".png" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=57" target="_blank" title="">Отчет об испытаниях 5.png</a> (Размер: 123.04 KB / Загрузок: 49)
<!-- end: postbit_attachments_attachment --><br />
<br />
<br />
Теперь у вас есть данные и вы можете соорудить таблицу, графики, мини-отчеты, использовать дальше для любых своих целей. <img src="https://community.winnum.io/images/smilies/cool.png" alt="Cool" title="Cool" class="smilie smilie_3" />]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Переход на страницу]]></title>
			<link>https://community.winnum.io/showthread.php?tid=34</link>
			<pubDate>Tue, 18 Mar 2025 12:32:15 +0300</pubDate>
			<dc:creator><![CDATA[<a href="https://community.winnum.io/member.php?action=profile&uid=8">Lamantur</a>]]></dc:creator>
			<guid isPermaLink="false">https://community.winnum.io/showthread.php?tid=34</guid>
			<description><![CDATA[Всем привет!<br />
Если мы хотим создать еще одну страницу динамического приложения, мы заранее продумываем где и когда будем ее открывать. Самый простой способ - кнопка, но возможны и более сложные <span style="font-style: italic;" class="mycode_i"><span style="text-decoration: line-through;" class="mycode_s">многоходовочки</span></span>.<br />
Итак: создали кнопку, зашли в настройки и создаем событие: "Переход на страницу" и получаем готовое решение, которое работает в большинстве случаев:<br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>&#36;("#63EC5ED4-9C93-4EA2-9EDF-36573CB1EEFC").click(function(event){<br />
    baseApplicationDesignerUtils.gotoPage('7CB5B62C-0810-4735-8936-E43D6BDA422F');<br />
});</code></div></div>Интерфейс редактора динамических приложений даже подсказывает нам название новой страницы и автоматом заменяет uuid - в коде ничего не надо править.<br />
Проблема возникает когда мы создаем несколько дистрибутивов одного и того же приложения, с теми же самыми страницами и импортируем его снова и снова, например таким образом сохраняем старые версии, чтобы опробовать разные варианты. Или просто хотим сохранить все версии для отслеживания изменений и подписываем все варианты приложений.<br />
В этом случае окажется, что когда импортируется новая страница приложения, то создаются для страниц новые uuid.  Для главной страницы тоже.  <img src="https://community.winnum.io/images/smilies/angry.png" alt="Angry" title="Angry" class="smilie smilie_11" /><br />
<br />
Можем обойти эту проблему, создав более сложный переход, но универсальный, бонусом мы сможем еще и что-то передать на другую страницу прямо в адресной строке - <br />
<br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>&#36;('#quality_button').click(function(event){<br />
    thisGoToPage("", "Например");<br />
});<br />
<br />
<br />
<br />
/*************************************************/<br />
function thisGoToPage( param, page_title ){<br />
    baseSdkUtils.service.WNDynamicApplicationHelper.getPageByTitle( <br />
        baseSdkUtils.the.appoid,<br />
        page_title,<br />
        function( data ){<br />
            var item = data.getElementsByTagName('item')[0];<br />
            var oid = item.getAttribute('elementId');  <br />
            window.location.href = '/Winnum/views/pages/app/dynamic/dgw.jsp?puuid=' + oid + '&amp;appid=' + baseSdkUtils.the.appoid + param + '&amp;mode=yes' ;<br />
        });<br />
}</code></div></div><br />
Функция thisGoToPage принимает 2 аргумента - параметр в виде строки, при этом можно писать несколько параметров через &amp;, например &amp;interval=day, он появится в адресной строке, откуда его легко прочитать и использовать. <br />
Ну и название страницы, тоже строковая переменная.<br />
baseSdkUtils.the.appoid - это переменная уже есть у вас с нужным значением, если вы подключали медиа библиотеку стандартным скриптом. Если нет - то посмотрите на адресную строку в вашем редакторе - oid - это он и есть.<br />
<br />
Теперь uuid и другие временные переменные не употребляются при переходе на страницу, у значит переход сработает всегда.]]></description>
			<content:encoded><![CDATA[Всем привет!<br />
Если мы хотим создать еще одну страницу динамического приложения, мы заранее продумываем где и когда будем ее открывать. Самый простой способ - кнопка, но возможны и более сложные <span style="font-style: italic;" class="mycode_i"><span style="text-decoration: line-through;" class="mycode_s">многоходовочки</span></span>.<br />
Итак: создали кнопку, зашли в настройки и создаем событие: "Переход на страницу" и получаем готовое решение, которое работает в большинстве случаев:<br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>&#36;("#63EC5ED4-9C93-4EA2-9EDF-36573CB1EEFC").click(function(event){<br />
    baseApplicationDesignerUtils.gotoPage('7CB5B62C-0810-4735-8936-E43D6BDA422F');<br />
});</code></div></div>Интерфейс редактора динамических приложений даже подсказывает нам название новой страницы и автоматом заменяет uuid - в коде ничего не надо править.<br />
Проблема возникает когда мы создаем несколько дистрибутивов одного и того же приложения, с теми же самыми страницами и импортируем его снова и снова, например таким образом сохраняем старые версии, чтобы опробовать разные варианты. Или просто хотим сохранить все версии для отслеживания изменений и подписываем все варианты приложений.<br />
В этом случае окажется, что когда импортируется новая страница приложения, то создаются для страниц новые uuid.  Для главной страницы тоже.  <img src="https://community.winnum.io/images/smilies/angry.png" alt="Angry" title="Angry" class="smilie smilie_11" /><br />
<br />
Можем обойти эту проблему, создав более сложный переход, но универсальный, бонусом мы сможем еще и что-то передать на другую страницу прямо в адресной строке - <br />
<br />
<div class="codeblock"><div class="title">Код:</div><div class="body" dir="ltr"><code>&#36;('#quality_button').click(function(event){<br />
    thisGoToPage("", "Например");<br />
});<br />
<br />
<br />
<br />
/*************************************************/<br />
function thisGoToPage( param, page_title ){<br />
    baseSdkUtils.service.WNDynamicApplicationHelper.getPageByTitle( <br />
        baseSdkUtils.the.appoid,<br />
        page_title,<br />
        function( data ){<br />
            var item = data.getElementsByTagName('item')[0];<br />
            var oid = item.getAttribute('elementId');  <br />
            window.location.href = '/Winnum/views/pages/app/dynamic/dgw.jsp?puuid=' + oid + '&amp;appid=' + baseSdkUtils.the.appoid + param + '&amp;mode=yes' ;<br />
        });<br />
}</code></div></div><br />
Функция thisGoToPage принимает 2 аргумента - параметр в виде строки, при этом можно писать несколько параметров через &amp;, например &amp;interval=day, он появится в адресной строке, откуда его легко прочитать и использовать. <br />
Ну и название страницы, тоже строковая переменная.<br />
baseSdkUtils.the.appoid - это переменная уже есть у вас с нужным значением, если вы подключали медиа библиотеку стандартным скриптом. Если нет - то посмотрите на адресную строку в вашем редакторе - oid - это он и есть.<br />
<br />
Теперь uuid и другие временные переменные не употребляются при переходе на страницу, у значит переход сработает всегда.]]></content:encoded>
		</item>
	</channel>
</rss>