UNPKG

dt-translate-angular

Version:

An Angular component/library for translating and rendering sign language gestures.

732 lines (718 loc) 27.7 kB
import * as i0 from '@angular/core'; import { Injectable, NgModule } from '@angular/core'; // src/translate-controller.ts class TranslateController { constructor() { this.appMode = 4; this.apiKey = ''; this.initialized = false; this.requestedText = ''; this.unityInstance = null; this.character = 0; this.speed = 0.025; this.backgroundColor = null; this.characterClothColor = null; this.selectedLanguage = 'en'; this.baseUrl = 'https://translateai.deaftawk.com:3001'; this.poseWorldLandmarks = 'false'; this.totalCharacters = 3; this.showLangDd = false; this.enableCharacterChange = true; this.unityCallback = null; this.windowVisible = true; this.windowWidth = 275; this.windowHeight = 155; this.isWindowVisible = true; this.appVersion = '1.0.0'; this.CHARACTER_POS_TEXT = 'standstill'; this.languages = ['en', 'da']; this.shouldShowLogs = true; window.addEventListener('focus', () => { this.showLogs('WINDOW FOCUS CALLED ============>'); if (this.requestedText === this.CHARACTER_POS_TEXT) return; const lastSentText = this.requestedText; this.requestedText = ''; if (lastSentText) { this.playPose({ text: lastSentText }, undefined, false); } }); } init(data) { this.showLogs('INIT CALLED ============>'); if (this.initialized) return; this.initialized = true; if (!data.key) throw new Error('API key is required'); this.apiKey = data.key; this.character = data.character || 0; this.appMode = [3, 4].indexOf(data.appMode) > -1 ? data.appMode : 4; this.backgroundColor = data.backgroundColor || null; this.characterClothColor = data.characterClothColor || null; this.speed = data.speed || 0.025; this.enableCharacterChange = data.chanageCharacter ?? true; this.showLangDd = data.showLanguageChangeOptions ?? false; this.selectedLanguage = 'en'; this.baseUrl = data.baseUrl || "https://translateai.deaftawk.com:3001"; this.poseWorldLandmarks = data.poseWorldLandmarks || 'false'; const dims = this.checkDimensions(data.width ? data.width : 0); this.showLogs(`DATA WIDTH ${data.width}`); this.showLogs(`WINDOW WIDTH HEIGHT ${dims.valid}`); if (dims.valid) { this.windowWidth = data.width; this.windowHeight = dims.height; } this.buildUI(); this.appMode === 4 ? this.initializeUnityWebGL() : this.playVideoMode(this.CHARACTER_POS_TEXT); } async buildUI() { /* ==========>> CONTAINER ELEMENT UI METHODS CALL <<============= */ this.showLogs('BUILDING UI ============>'); const characterContainer = await this.createCharacterChangeBtns(); const crossButton = await this.createCrossButton(); const versionContainer = await this.createVersionContainer(); const miniBtn = await this.createMiniButton(); const loader = await this.createLoader(); const slider = await this.createSlider(); const container = document.createElement('div'); container.id = 'webgl_m_c'; container.style.position = 'absolute'; container.style.top = '50px'; container.style.left = '50px'; container.style.width = `${this.windowWidth}px`; container.style.height = `${this.windowHeight}px`; container.style.border = '1px solid #ccc'; container.style.borderRadius = '5px'; container.style.cursor = 'grab'; container.style.display = 'flex'; container.style.zIndex = '99999'; container.style.background = '#fff'; container.addEventListener('mouseover', () => this.toggleControls(true)); container.addEventListener('mouseout', () => this.toggleControls(false)); /* ==========>> CONTAINER UI <<============= */ document.body.appendChild(container); document.body.appendChild(miniBtn); this.makeCanvasDraggable(container); /* ==========>> CONTAINER ELEMENT UI APPEND <<============= */ container.append(characterContainer); container.append(crossButton); container.append(versionContainer); container.append(loader); container.append(slider); /* ----------------------*******---------------------- */ this.showLogs(`MODE ============>${this.appMode}`); if (this.appMode === 3) { const video = document.createElement('video'); video.id = 'signLanguagePlayer'; video.style.width = `${this.windowWidth}px`; video.style.height = `${this.windowHeight}px`; video.style.borderRadius = '5px'; video.style.objectFit = 'cover'; video.muted = true; video.autoplay = true; video.loop = true; video.playsInline = true; container.appendChild(video); } else { const canvas = document.createElement('canvas'); canvas.id = 'unityCanvas'; canvas.style.width = `${this.windowWidth}px`; canvas.style.height = `${this.windowHeight}px`; canvas.style.borderRadius = '5px'; canvas.style.display = 'none'; container.appendChild(canvas); } } initializeUnityWebGL() { const buildUrl = 'https://d1g156dmclqesu.cloudfront.net/vanilla/Build'; const loaderUrl = `${buildUrl}/webglbuild.loader.js`; const config = { dataUrl: `${buildUrl}/webglbuild.data`, frameworkUrl: `${buildUrl}/webglbuild.framework.js`, codeUrl: `${buildUrl}/webglbuild.wasm`, streamingAssetsUrl: 'StreamingAssets', companyName: 'DeafTawk', productName: 'Translate', productVersion: '1.0.2', }; const script = document.createElement('script'); script.src = loaderUrl; script.onload = () => { //@ts-ignore createUnityInstance(document.getElementById('unityCanvas'), config, () => { }).then(instance => { this.unityInstance = instance; this.playPose({ text: this.CHARACTER_POS_TEXT }); }); }; document.body.appendChild(script); } playPose(data, callback, showLoader = true) { this.showLogs(`PLSY PODR CALLED ============>`); if (!data.text || data.text === this.requestedText) { callback?.("No text provided to play pose"); return; } this.showLogs(`PLAYING POSE ============>${data.text}`); this.requestedText = data.text; callback ? this.unityCallback = callback : ''; if (showLoader) this.toggleLoader(true); this.showLogs(`PLAYING POSE 1============>${data.text}`); if (data.language && ['en', 'da'].indexOf(data.language) > -1) { this.selectedLanguage = data.language; } this.showLogs(`PLAYING POSE 2============>this.appMode${this.appMode}`); if (this.appMode === 3) { this.playVideoMode(data.text); } else { this.showLogs(`INSIDE MODE 4 CHECING UNITY INSTANCE`); if (this.unityInstance) { const payload = { text: data.text, baseUrl: this.baseUrl, poseWorldLandmarks: this.poseWorldLandmarks, character: this.character, nextFrameTime: this.speed, apiKey: this.apiKey, backgroundColor: this.backgroundColor, characterClothColor: this.characterClothColor, language: this.selectedLanguage, }; this.showLogs(`DATA FOR UNITY REQ ${JSON.stringify(payload)}`); this.unityInstance.SendMessage('NetworkManager', 'RecievePoseText', JSON.stringify(payload)); } else { this.showLogs(`NO UNITY INSTANCE RETRYING...`); setTimeout(() => this.playPose(data, callback), 1000); } } } async playVideoMode(text, showLoader = true) { const signed = this.selectedLanguage === 'en' ? 'ase' : 'dsl'; if (showLoader) this.toggleLoader(true); const baseUrl = this.baseUrl; const url = `${baseUrl}/spoken_text_to_signed_pose?text=${encodeURIComponent(text)}&spoken=${this.selectedLanguage}&signed=${signed}&myown=3&character=${this.character}&apikey=${this.apiKey}`; try { const res = await fetch(url); const blob = await res.blob(); const video = document.getElementById('signLanguagePlayer'); if (video) { video.src = URL.createObjectURL(blob); } this.toggleLoader(false); this.unityCallback?.(''); } catch (err) { this.toggleLoader(false); console.error(err); this.unityCallback?.(''); } } showWindow() { const mainWindow = document.getElementById('webgl_m_c'); const minibtn = document.getElementById('sw_btn'); if (mainWindow) mainWindow.style.display = 'flex'; if (minibtn) minibtn.style.display = 'none'; } hideWindow() { const mainWindow = document.getElementById('webgl_m_c'); const minibtn = document.getElementById('sw_btn'); if (mainWindow) mainWindow.style.display = 'none'; if (minibtn) minibtn.style.display = 'flex'; } toggleControls(show) { const header = document.getElementById('webgl_header'); const version = document.getElementById('versionContainer'); const charContainer = document.getElementById('characterContainer'); if (!header || !version || !charContainer) return; if (show) { header.style.display = 'flex'; version.style.display = 'none'; if (this.requestedText && this.enableCharacterChange) { charContainer.style.display = 'flex'; } } else { header.style.display = 'none'; version.style.display = 'flex'; charContainer.style.display = 'none'; } } handleWebglError(error) { this.showLogs('ERROR RECIEVED FROM UNITY'); if (typeof this.unityCallback === 'function') { this.unityCallback(error); } console.error('WEBGL-ERROR ----> ', error); } onDataReceived(message) { this.showLogs('DATA RECIEVED FROM UNITY'); this.toggleLoader(false); const unityCanvas = document.getElementById('unityCanvas'); const loader = document.getElementById('webgl_loader'); if (loader) loader.style.display = 'none'; if (unityCanvas) { unityCanvas.style.display = 'flex'; unityCanvas.style.cursor = 'grab'; } const langDropdown = document.getElementById('webgl-lang-dd'); if (langDropdown) { langDropdown.style.display = this.showLangDd ? 'block' : 'none'; } if (!this.requestedText || this.requestedText === this.CHARACTER_POS_TEXT) return; const textContainer = document.getElementById('t_c_slider'); if (textContainer) { textContainer.textContent = this.requestedText; } const slider = document.getElementById('webgl_slider'); if (slider) { slider.style.display = 'flex'; } if (typeof this.unityCallback === 'function') { this.unityCallback(''); } const banner = document.getElementById('webgl_bnr'); if (banner) { banner.style.display = 'none'; } } checkDimensions(width) { const minWidth = 275; const maxWidth = 500; const baseHeight = 155; const ratio = baseHeight / minWidth; if (width <= 0) { return { valid: false, height: baseHeight }; } if (width < minWidth || width > maxWidth) return { valid: false, height: baseHeight }; return { valid: true, height: Math.round(width * ratio) }; } createCharacterChangeBtns() { const characterContainer = document.createElement("div"); characterContainer.id = "characterContainer"; characterContainer.style.position = "absolute"; characterContainer.style.width = `${this.windowWidth}px`; characterContainer.style.textAlign = "center"; characterContainer.style.display = "none"; characterContainer.style.justifyContent = "center"; characterContainer.style.top = "40%"; const btnsParent = document.createElement("div"); btnsParent.style.width = `${this.windowWidth}px`; btnsParent.style.textAlign = "center"; btnsParent.style.display = "flex"; btnsParent.style.justifyContent = "space-between"; const prevCharacter = document.createElement("div"); prevCharacter.className = "round-btn"; prevCharacter.id = "prevCharacter"; prevCharacter.textContent = "◀"; prevCharacter.title = "Previous character"; const nextCharacter = document.createElement("div"); nextCharacter.className = "round-btn"; nextCharacter.id = "nextCharacter"; nextCharacter.textContent = "▶"; nextCharacter.title = "Next character"; const roundButtonStyle = ` width: 28px; height: 28px; border-radius: 16px; display: flex; justify-content: center; align-items: center; border: 1px solid navajowhite; background-color: #d4dceb; font-size: 11px; color: gray; cursor: pointer; `; prevCharacter.style.cssText = roundButtonStyle; nextCharacter.style.cssText = roundButtonStyle; btnsParent.appendChild(prevCharacter); btnsParent.appendChild(nextCharacter); characterContainer.appendChild(btnsParent); nextCharacter.addEventListener("click", () => { this.handleCharacterChange('next'); }); prevCharacter.addEventListener("click", () => { this.handleCharacterChange('prev'); }); return characterContainer; } handleCharacterChange(action) { const data = { text: this.requestedText }; switch (action) { case 'next': this.character = (this.character + 1) % this.totalCharacters; this.requestedText = ''; this.playPose(data); break; default: this.character = (this.character - 1 + this.totalCharacters) % this.totalCharacters; this.requestedText = ''; this.playPose(data); break; } } createCrossButton() { const div = document.createElement('div'); div.id = 'webgl_header'; div.style.position = 'absolute'; div.style.width = "24px"; div.style.height = "24px"; div.style.display = "none"; div.style.justifyContent = "center"; div.style.alignItems = "center"; div.style.right = '5px'; div.style.cursor = 'pointer'; const crossSymbol = document.createElement('span'); crossSymbol.textContent = '×'; crossSymbol.style.fontSize = '20px'; crossSymbol.style.color = '#000'; crossSymbol.style.userSelect = 'none'; div.appendChild(crossSymbol); div.addEventListener('click', () => { this.handleWindowToggle(); }); return div; } handleWindowToggle() { if (this.isWindowVisible) { this.isWindowVisible = false; this.hideWindow(); this.toggleLoader(false); return; } this.isWindowVisible = true; this.showWindow(); } createVersionContainer() { const versionContainer = document.createElement('span'); versionContainer.id = 'versionContainer'; versionContainer.textContent = `V-${this.appVersion}`; versionContainer.style.display = "flex"; versionContainer.style.position = "absolute"; versionContainer.style.zIndex = "9"; versionContainer.style.right = "10px"; versionContainer.style.top = "6px"; versionContainer.style.color = "#d7d1d1"; versionContainer.style.fontSize = "12px"; return versionContainer; } makeCanvasDraggable(canvas) { let offsetX, offsetY, isDragging = false; canvas.addEventListener('mousedown', (event) => { isDragging = true; offsetX = event.clientX - canvas.offsetLeft; offsetY = event.clientY - canvas.offsetTop; canvas.style.cursor = "grabbing"; }); document.addEventListener('mousemove', (event) => { if (isDragging) { canvas.style.left = event.clientX - offsetX + 'px'; canvas.style.top = event.clientY - offsetY + 'px'; } }); document.addEventListener('mouseup', () => { isDragging = false; canvas.style.cursor = "grab"; }); } createMiniButton() { const div = document.createElement('div'); div.id = 'sw_btn'; div.style.position = 'absolute'; div.style.bottom = '5px'; div.style.right = '5px'; div.style.width = "34px"; div.style.height = "34px"; div.style.border = "1px solid #cec6c6"; div.style.cursor = 'pointer'; div.style.display = "none"; div.style.justifyContent = "center"; div.style.alignItems = "center"; div.style.animation = 'bounce 3s infinite'; div.style.backgroundColor = '#aba3a3'; div.style.borderRadius = '50%'; div.title = 'Show Translate window'; const arrowUp = document.createElement('span'); arrowUp.textContent = '↑'; arrowUp.style.fontSize = '20px'; arrowUp.style.color = '#000'; arrowUp.style.userSelect = 'none'; div.appendChild(arrowUp); div.addEventListener('click', () => { this.handleWindowToggle(); }); const style = document.createElement('style'); style.textContent = ` @keyframes bounce { 0%, 20%, 50%, 80%, 100% { transform: translateY(0); } 40% { transform: translateY(-10px); } 60% { transform: translateY(-5px); } } `; document.head.appendChild(style); return div; } createLoader() { let loader = document.createElement('div'); loader.id = 'webgl_loader'; loader.style.position = 'absolute'; loader.style.width = `${this.windowWidth}px`; loader.style.height = `${this.windowHeight}px`; loader.style.cursor = 'grab'; loader.style.display = 'flex'; loader.style.justifyContent = 'center'; loader.style.alignItems = 'center'; loader.style.zIndex = '1'; let spinner = document.createElement('div'); spinner.style.border = '4px solid #f3f3f3'; spinner.style.borderTop = '4px solid #3498db'; spinner.style.borderRadius = '50%'; spinner.style.width = '28px'; spinner.style.height = '28px'; spinner.style.animation = 'spin 1s linear infinite'; spinner.style.borderColor = '#2a4b8d #2a4b8d rgb(255, 255, 255)'; loader.appendChild(spinner); const styleSheet = document.createElement('style'); styleSheet.type = 'text/css'; styleSheet.innerText = ` @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } `; document.head.appendChild(styleSheet); return loader; } createSlider() { const div = document.createElement('div'); div.id = 'webgl_slider'; Object.assign(div.style, { position: 'absolute', width: `${this.windowWidth - 11}px`, height: "24px", display: "none", justifyContent: "flex-start", alignItems: "center", overflow: 'hidden', padding: '3px 6px', backgroundColor: '#000', fontSize: '12px', textAlign: 'center', bottom: '-1px', borderBottomRightRadius: '5px', borderBottomLeftRadius: '5px', color: '#fff' }); const text = document.createElement('span'); text.id = "t_c_slider"; text.textContent = ''; Object.assign(text.style, { display: 'inline-block', whiteSpace: 'nowrap', position: 'relative', paddingLeft: '100%', animation: 'slideLeft linear infinite' }); div.appendChild(text); const style = document.createElement('style'); style.textContent = ` @keyframes slideLeft { 0% { transform: translateX(0); } 100% { transform: translateX(-100%); } } `; document.head.appendChild(style); const resizeObserver = new ResizeObserver(() => { this.updateSliderAnimation(div, text); }); resizeObserver.observe(text); return div; } updateSliderAnimation(container, textElement, pixelsPerSecond = 50) { textElement.style.animation = 'none'; void textElement.offsetWidth; const containerWidth = container.offsetWidth; const textWidth = textElement.scrollWidth; const distance = containerWidth + textWidth; const duration = distance / pixelsPerSecond; textElement.style.animation = `slideLeft ${duration}s linear infinite`; } toggleLoader(show) { const loader = document.getElementById('webgl_loader'); if (!loader) return; if (show) { loader.style.display = 'flex'; } else { loader.style.display = 'none'; } } createLanguageDropdown() { const style = document.createElement('style'); style.textContent = ` .dropdown-container { position: relative; width: 65px; font-family: sans-serif; font-size: 12px; top: 2px; left: 3px; display: none; } .dropdown-selected { padding: 3px; background: #f0f0f0; border: 1px solid #ccc; cursor: pointer; border-radius: 2px; display: flex; justify-content: center; align-items: center; color: #424a9f; } .dropdown-list { position: absolute; top: 100%; left: 0; right: 0; background: white; border: 1px solid #ccc; height: 56px; max-height: 130px; overflow-y: auto; margin-top: 4px; border-radius: 6px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); z-index: 100; } .dropdown-item { padding: 6px; cursor: pointer; display: flex; justify-content: center; align-items: center; } .dropdown-item:hover { background-color: #424a9f; color: #fff } .hidden { display: none; } .dropdown-list::-webkit-scrollbar { width: 6px; } .dropdown-list::-webkit-scrollbar-thumb { background-color: #888; border-radius: 3px; } .dropdown-list::-webkit-scrollbar-thumb:hover { background-color: #555; } `; document.head.appendChild(style); const container = document.createElement('div'); container.className = 'dropdown-container'; container.id = 'webgl-lang-dd'; const selected = document.createElement('div'); selected.className = 'dropdown-selected'; selected.textContent = this.languages[0].toUpperCase(); const list = document.createElement('div'); list.className = 'dropdown-list hidden'; this.languages.forEach(lang => { const item = document.createElement('div'); item.className = 'dropdown-item'; item.textContent = lang.toUpperCase(); item.addEventListener('click', () => { selected.textContent = lang.toUpperCase(); this.selectedLanguage = lang; list.classList.add('hidden'); }); list.appendChild(item); }); selected.addEventListener('click', () => { list.classList.toggle('hidden'); }); container.appendChild(selected); container.appendChild(list); document.addEventListener('click', (e) => { const target = e.target; if (container && !container.contains(target)) { list.classList.add('hidden'); } }); return container; } showLogs(message) { if (!this.shouldShowLogs) return; console.log('DT ANGUALR SDK:', message); } } const Translate = new TranslateController(); window.onDataRecieved = (message) => { Translate.onDataReceived(message); }; class TranslateService { init(options) { this.controller = new TranslateController(); this.controller.init(options); } playPose(data, callback) { this.controller?.playPose(data, callback); } showWindow(text) { this.controller?.showWindow(); } hideWindow(text) { this.controller?.hideWindow(); } } TranslateService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TranslateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); TranslateService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TranslateService, providedIn: 'root' }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TranslateService, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }] }); class TranslateModule { } TranslateModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TranslateModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); TranslateModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: TranslateModule }); TranslateModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TranslateModule, providers: [TranslateService] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TranslateModule, decorators: [{ type: NgModule, args: [{ providers: [TranslateService] }] }] }); /** * Generated bundle index. Do not edit. */ export { TranslateModule, TranslateService }; //# sourceMappingURL=dt-translate-angular.mjs.map