diff --git a/.gitignore b/.gitignore index 10bd2523..aa1e3097 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,6 @@ srtm/ frontend/static/js/archives.js frontend/static/js/flyxc.js frontend/static/js/status.js -frontend/static/js/tracking.js \ No newline at end of file +frontend/static/js/tracking.js +frontend/static/js/xc-score-worker.js + diff --git a/.vscode/launch.json b/.vscode/launch.json index dcd56b43..9512caa4 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,6 +4,13 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { + "name": "Launch Chrome", + "request": "launch", + "type": "pwa-chrome", + "url": "http://localhost:8080/?track=https%3A%2F%2Fparapente.ffvl.fr%2Fsites%2Fparapente.ffvl.fr%2Ffiles%2Figcfiles%2F2020-06-10-igcfile-276534-200361.igc&s=20&l=xc&p=u%7BpiGiuik%40%3FvfbB", + "webRoot": "${workspaceFolder}/frontend/static" + }, { "type": "node", "request": "attach", @@ -12,6 +19,6 @@ "outFiles": [ "${workspaceFolder}/**/*.js" ], - } + }, ] } \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index b778bcb4..a63b8d57 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -93,6 +93,14 @@ "@ui5/webcomponents-base": "0.20.0" } }, + "collections": { + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/collections/-/collections-5.1.11.tgz", + "integrity": "sha512-WbBP7RRuAwnZHNTcDsUEHZxcowEWMfI0r2iN0okRS147IAvJsR6EAi1+b/C0ytmCtGne456azwgZ1RZsBxWSiw==", + "requires": { + "weak-map": "~1.0.x" + } + }, "cookiesjs": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/cookiesjs/-/cookiesjs-3.0.3.tgz", @@ -136,6 +144,24 @@ "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", "dev": true }, + "flatbush": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/flatbush/-/flatbush-3.3.0.tgz", + "integrity": "sha512-F3EzQvKpdmXUbFwWxLKBpytOFEGYQMCTBLuqZ4GEajFOEAvnOIBiyxW3OFSZXIOtpCS8teN6bFEpNZtnVXuDQA==", + "requires": { + "flatqueue": "^1.2.0" + } + }, + "flatqueue": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/flatqueue/-/flatqueue-1.2.1.tgz", + "integrity": "sha512-X86TpWS1rGuY7m382HuA9vngLeDuWA9lJvhEG+GfgKMV5onSvx5a71cl7GMbXzhWtlN9dGfqOBrpfqeOtUfGYQ==" + }, + "flight-recorder-manufacturers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/flight-recorder-manufacturers/-/flight-recorder-manufacturers-1.1.0.tgz", + "integrity": "sha1-ZmOdXT8zzawUfj/jWnARqJm1Bfk=" + }, "geolib": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/geolib/-/geolib-3.3.1.tgz", @@ -146,6 +172,25 @@ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" }, + "igc-parser": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/igc-parser/-/igc-parser-0.5.0.tgz", + "integrity": "sha512-XpuPcl7MTu3H+FPjOpH21AQhZWOeaqFpk/T5M5r63ROxRpLF9idgb6SS7/NyQHXOe1L1pdgDKIErKfkxqp90OA==", + "requires": { + "flight-recorder-manufacturers": "^1.0.1" + } + }, + "igc-xc-score": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/igc-xc-score/-/igc-xc-score-1.5.0.tgz", + "integrity": "sha512-hWfx1IfxuDyCPKdco/8T87PRcl1paKmPbOGlp5zzUNuzKBCdBxuDY1UtU0kfMLE2B3fnlnzNofpJ/qlAsKFzCQ==", + "requires": { + "collections": "^5.1.11", + "flatbush": "^3.3.0", + "igc-parser": "^0.5.0", + "rbush": "^3.0.1" + } + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -191,6 +236,19 @@ "resolved": "https://registry.npmjs.org/pwa-helpers/-/pwa-helpers-0.9.1.tgz", "integrity": "sha512-4sP/C9sSxQ3w80AATmvCEI3R+MHzCwr2RSZEbLyMkeJgV3cRk7ySZRUrQnBDSA7A0/z6dkYtjuXlkhN1ZFw3iA==" }, + "quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" + }, + "rbush": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz", + "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", + "requires": { + "quickselect": "^2.0.0" + } + }, "redux": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz", @@ -255,6 +313,11 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/url-search-params-polyfill/-/url-search-params-polyfill-5.1.0.tgz", "integrity": "sha512-yjFY7uw2xRf9e8Mg4ZVkZwtp8dMCC4cbBkEIZiTDpuSY2WJ9+Quw0wRhxncv32qaMQwmBQT+P847rO8PrFhhDA==" + }, + "weak-map": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/weak-map/-/weak-map-1.0.5.tgz", + "integrity": "sha1-eWkVhNmGB/UHC9O3CkDmuyLkAes=" } } } diff --git a/frontend/package.json b/frontend/package.json index efb2efbd..0d63130b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -16,6 +16,7 @@ "d3-array": "^2.4.0", "d3-scale-chromatic": "^1.5.0", "geolib": "^3.3.1", + "igc-xc-score": "^1.5.0", "lit-element": "^2.3.1", "lit-html": "^1.2.1", "pbf": "^3.2.1", diff --git a/frontend/src/viewer/components/path-element.ts b/frontend/src/viewer/components/path-element.ts index 5e4edf0b..37e2c479 100644 --- a/frontend/src/viewer/components/path-element.ts +++ b/frontend/src/viewer/components/path-element.ts @@ -2,14 +2,18 @@ import { CSSResult, LitElement, PropertyValues, TemplateResult, css, customEleme import { Measure, Point } from '../logic/score/measure'; import { RootState, store } from '../store'; import { setDistance, setLeague, setScore, setSpeed } from '../actions/map'; +import { Track } from '../logic/map'; -import { CircuitType } from '../logic/score/scorer'; +import { CircuitType, Score } from '../logic/score/scorer'; import { ClosingSector } from '../gm/closing-sector'; import { FaiSectors } from '../gm/fai-sectors'; import { LEAGUES } from '../logic/score/league/leagues'; import { PlannerElement } from './planner-element'; import { connect } from 'pwa-helpers'; import { formatUnit } from '../logic/units'; +//import { isMobileDevice } from '../logic/util'; +import { BRecord, RecordExtensions } from 'igc-parser'; +import { Solution } from 'igc-xc-score'; import { Units } from '../reducers/map'; const ROUTE_STROKE_COLORS = { @@ -19,6 +23,13 @@ const ROUTE_STROKE_COLORS = { [CircuitType.FAI_TRIANGLE]: '#ffff00', }; +const SCORE_STROKE_COLORS = { + [CircuitType.OPEN_DISTANCE]: '#b22222', + [CircuitType.OUT_AND_RETURN]: '#b22222', + [CircuitType.FLAT_TRIANGLE]: '#cd5c5c', + [CircuitType.FAI_TRIANGLE]: '#cd5c5c', +}; + const CIRCUIT_SHORT_NAME = { [CircuitType.OPEN_DISTANCE]: 'od', [CircuitType.OUT_AND_RETURN]: 'oar', @@ -37,10 +48,27 @@ const WAYPOINT_FORMATS: { [id: string]: string } = { @customElement('path-ctrl-element') export class PathCtrlElement extends connect(store)(LitElement) { line: google.maps.Polyline | null = null; + scoring: { + path: google.maps.Polyline | null; + closing: google.maps.Polyline | null; + } = { path: null, closing: null }; @property({ attribute: false }) expanded = false; + + @property({ attribute: false }) + tracks: Track[] | null = null; + + @property({ attribute: false }) + currentTrack: number | null = null; + + @property({ attribute: false }) + worker: Worker | null = null; + + @property({ attribute: false }) + measureIcon: string = 'img/measuring.svg'; + @property({ attribute: false }) units: Units | null = null; @@ -104,6 +132,8 @@ export class PathCtrlElement extends connect(store)(LitElement) { this.speed = state.map.speed; this.league = state.map.league; this.units = state.map.units; + this.tracks = state.map.tracks; + this.currentTrack = state.map.currentTrack; } } @@ -176,14 +206,10 @@ export class PathCtrlElement extends connect(store)(LitElement) { } else { const line = this.line as google.maps.Polyline; line.setMap(null); - if (this.flight) { - this.flight.setMap(null); - } - if (this.closingSector) { - this.closingSector.setMap(null); - } - if (this.faiSectors) { - this.faiSectors.setMap(null); + for (let e of [this.flight, this.closingSector, this.faiSectors, this.scoring.closing, this.scoring.path]) { + if (e) { + e.setMap(null); + } } store.dispatch(setScore(null)); google.maps.event.removeListener(this.onAddPoint as google.maps.MapsEventListener); @@ -333,13 +359,116 @@ export class PathCtrlElement extends connect(store)(LitElement) { } } + protected launchScoring(): void { + if (this.tracks && this.currentTrack !== null && this.tracks[this.currentTrack]) { + const fixes: BRecord[] = []; + for (let i = 0; i < this.tracks[this.currentTrack].fixes.lat.length; i++) + // Keep this to the bare minimum needed for igc-xc-score + fixes[i] = { + timestamp: this.tracks[this.currentTrack].fixes.ts[i], + latitude: this.tracks[this.currentTrack].fixes.lat[i], + longitude: this.tracks[this.currentTrack].fixes.lon[i], + pressureAltitude: this.tracks[this.currentTrack].fixes.alt[i], + gpsAltitude: this.tracks[this.currentTrack].fixes.alt[i], + valid: true, + extensions: {} as RecordExtensions, + fixAccuracy: null, + time: '', + enl: null + }; + if (this.worker !== null) + this.worker.terminate(); + this.worker = new Worker('js/xc-score-worker.js'); + this.worker.onmessage = this.updateScore.bind(this); + this.worker.postMessage({ msg: 'xc-score-start', flight: JSON.stringify(fixes), league: this.league }); + this.measureIcon = 'img/pacman.svg'; + } + } + + protected updateScore(msg: any): void { + if (msg.data.msg && (msg.data.msg === 'xc-score-result' || msg.data.msg === 'xc-score-progress')) { + const r = JSON.parse(msg.data.r) as Solution; + let t: CircuitType; + let closedCircuit: boolean = false; + switch (r.opt.scoring.code) { + case 'tri': + t = CircuitType.FLAT_TRIANGLE; + closedCircuit = true; + break; + case 'fai': + t = CircuitType.FAI_TRIANGLE; + closedCircuit = true; + break; + default: + case 'od': + t = CircuitType.OPEN_DISTANCE; + break; + } + const score: Score = { + points: r.score ? r.score : 0, + distance: r.scoreInfo ? r.scoreInfo.distance * 1000 : 0, + closingRadius: r.scoreInfo && r.scoreInfo.cp ? r.scoreInfo.cp.d : 0, + multiplier: r.opt.scoring.multiplier, + circuit: t, + indexes: [0] + } + + if (this.scoring.path !== null) + this.scoring.path.setMap(null); + if (this.scoring.closing !== null) + this.scoring.closing.setMap(null); + let turnPoints: google.maps.LatLng[] = []; + if (r.scoreInfo && r.scoreInfo.tp) { + turnPoints = r.scoreInfo.tp.map(p => new google.maps.LatLng(p.y, p.x)); + if (closedCircuit) { + /* Triangle -> first point is also last */ + turnPoints.push(turnPoints[0]); + } else { + /* Open distance -> ep.start and ep.finish are actually first and fifth turnpoint */ + if (r.scoreInfo && r.scoreInfo.ep) { + turnPoints.unshift(new google.maps.LatLng(r.scoreInfo.ep.start.y, r.scoreInfo.ep.start.x)); + turnPoints.push(new google.maps.LatLng(r.scoreInfo.ep.finish.y, r.scoreInfo.ep.finish.x)); + } + } + this.scoring.path = new google.maps.Polyline({ + map: this.map as google.maps.Map, + path: turnPoints, + strokeColor: SCORE_STROKE_COLORS[score.circuit], + strokeWeight: 4, + zIndex: 1000, + }); + } + let closingPoints: google.maps.LatLng[] = []; + if (r.scoreInfo && r.scoreInfo.cp && closedCircuit) { + closingPoints = [new google.maps.LatLng(r.scoreInfo.cp.in.y, r.scoreInfo.cp.in.x), + new google.maps.LatLng(r.scoreInfo.cp.out.y, r.scoreInfo.cp.out.x)]; + this.scoring.closing = new google.maps.Polyline({ + map: this.map as google.maps.Map, + path: closingPoints, + strokeColor: SCORE_STROKE_COLORS['Out and return'], + strokeWeight: 4, + zIndex: 1000, + }); + } + + store.dispatch(setScore(score)); + if (msg.data.msg === 'xc-score-result') + this.measureIcon = 'img/measuring.svg'; + } + } + protected render(): TemplateResult { // Update the URL on re-rendering this.getQrText(); return this.units ? html` <link rel="stylesheet" href="https://kit-free.fontawesome.com/releases/latest/css/free.min.css" /> - <span .hidden=${!this.expanded}>${formatUnit(this.distance, this.units.distance)}</span> + <span .hidden=${!this.expanded}> + <i class="fas fa-2x" style="cursor: pointer" @click=${this.launchScoring}> + <img width="32" height="32" style="vertical-align: middle;" src="${this.measureIcon}" /> + </i> + ${formatUnit(this.distance, this.units.distance)} + </span> <i class="fas fa-ruler fa-2x" style="cursor: pointer" @click=${this.toggleExpanded}></i> <ui5-dialog id="share-dialog" header-text="Share"> diff --git a/frontend/src/viewer/components/tracking-element.ts b/frontend/src/viewer/components/tracking-element.ts index 37c0d4dd..c91d9760 100644 --- a/frontend/src/viewer/components/tracking-element.ts +++ b/frontend/src/viewer/components/tracking-element.ts @@ -67,7 +67,8 @@ export class TrackingElement extends connect(store)(LitElement) { this.features = map.data.addGeoJson(geoJson); features.forEach((f) => map.data.remove(f)); } - }); + }) + .catch((e) => { console.error(e); return null; }); } protected setupListener(map: google.maps.Map): void { diff --git a/frontend/src/viewer/logic/util.ts b/frontend/src/viewer/logic/util.ts new file mode 100644 index 00000000..9e8608bf --- /dev/null +++ b/frontend/src/viewer/logic/util.ts @@ -0,0 +1,3 @@ +export function isMobileDevice() { + return (typeof window.orientation !== "undefined") || (navigator.userAgent.indexOf('IEMobile') !== -1); +} \ No newline at end of file diff --git a/frontend/src/workers/xc-score-worker.ts b/frontend/src/workers/xc-score-worker.ts new file mode 100644 index 00000000..44bdbbf6 --- /dev/null +++ b/frontend/src/workers/xc-score-worker.ts @@ -0,0 +1,42 @@ +import { solver, scoringRules } from 'igc-xc-score'; +import { IGCFile } from 'igc-parser'; + +/* The joy of JS multithreading */ +function filterFunc(o: any): object { + const r: any = {}; + for (let k of Object.keys(o)) { + if (typeof o[k] === 'function' || typeof o[k] === 'undefined' || o[k] === null) + continue; + else if (o[k] instanceof Array) + r[k] = o[k]; + else if (typeof o[k] === 'object') + r[k] = filterFunc(o[k]); + else + r[k] = o[k]; + } + return r; +} + +self.onmessage = function (msg: any) { + if (msg.data.msg = 'xc-score-start') { + const flight = <IGCFile>{ fixes: JSON.parse(msg.data.flight) }; + let rules: object | undefined = undefined; + switch (msg.data.league) { + case 'xc': + rules = scoringRules.XContest; + break; + case 'fr': + rules = scoringRules.FFVL; + break; + } + if (rules === undefined) + return; + const it = solver(flight, rules, { maxcycle: 1000 }); + let s = it.next(); + while (!s.done) { + self.postMessage({ msg: 'xc-score-progress', r: JSON.stringify(filterFunc(s.value)) }); + s = it.next(); + } + self.postMessage({ msg: 'xc-score-result', r: JSON.stringify(filterFunc(s.value)) }); + } +}; \ No newline at end of file diff --git a/frontend/static/img/measuring.svg b/frontend/static/img/measuring.svg new file mode 100644 index 00000000..6f91ae5d --- /dev/null +++ b/frontend/static/img/measuring.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 125" enable-background="new 0 0 100 100" xml:space="preserve"><g><path d="M90.129,88.626H70.792c0,0,15.829-16.607,16.355-17.283c1.927-2.465,1.768-7.104-0.24-9.996 c-2.338-3.916-7.416-9.338-15.354-14.192c-0.093-0.065-37.969-22.113-37.969-22.113c-0.087-0.051-0.149-0.065-0.229-0.105 c-0.133-0.068-0.441-0.197-0.459-0.202c-1.766-0.646-3.811-0.07-4.938,1.544c-1.341,1.924-0.869,4.571,1.055,5.911l23.993,16.723 c-1.201,1.261-2.696,3.604-2.696,6.396v20.726l-21.144,12.37c-0.2,0.116-0.347,0.23-0.47,0.342 c-1.764,1.363-2.299,3.843-1.142,5.819c0.838,1.432,2.343,2.229,3.89,2.229c0.772,0,1.556-0.199,2.271-0.618l23.373-13.674 c1.938-1.237,2.181-3.231,2.184-3.249l2.176-15.218c1.173,0.488,2.436,1.113,3.615,1.898c1.243,0.83,2.476,1.966,3.568,3.084 L56.172,88.771c-0.578,0.873-1.868,2.401-1.142,5.618c0.466,2.078,2.851,3.912,4.77,3.912h30.329c2.671,0,4.839-2.162,4.839-4.844 C94.968,90.79,92.8,88.626,90.129,88.626z"/><circle cx="39.758" cy="56.051" r="9.173"/></g><g><path d="M40.473,11.409c-0.008-0.133-0.012-0.268-0.012-0.403c0-5.445-4.429-9.874-9.872-9.874h-11.79 c-5.445,0-9.875,4.429-9.875,9.874v87.79H23.57v-87.79c0-1.927,0.781-3.672,2.043-4.942c0.131,0.143,0.261,0.287,0.397,0.431 c1.126,1.186,2.098,2.21,2.098,4.511c0,5.444,4.43,9.875,9.876,9.875h9.378v-0.006c0.04,0.001,0.079,0.006,0.119,0.006 c4.614,0,8.674-3.269,9.649-7.774l0.376-1.733L40.473,11.409z M20.714,11.006V95.94H11.78v-4.799h6.197v-2.939H11.78v-4.821h3.386 v-2.939H11.78v-4.822h6.197V72.68H11.78v-4.82h3.386V64.92H11.78v-4.822h6.197v-2.939H11.78v-4.82h3.386v-2.939H11.78v-4.823h6.197 v-2.94H11.78v-4.822h3.386v-2.939H11.78V29.05h6.197v-2.94H11.78v-4.821h3.386v-2.94H11.78v-4.823h6.197v-2.94h-6.154 c0.222-3.671,3.25-6.598,6.977-6.598h4.85C21.837,5.779,20.714,8.265,20.714,11.006z M37.984,18.025 c-3.871,0-7.021-3.149-7.021-7.019c0-3.44-1.666-5.195-2.882-6.477c-0.014-0.016-0.027-0.029-0.041-0.045 c0.792-0.311,1.648-0.496,2.548-0.496c3.869,0,7.017,3.148,7.017,7.018c0,2.744,1.127,5.228,2.94,7.019H37.984z M47.481,18.025 c-2.696,0-5.042-1.527-6.218-3.762l12.449-0.027C52.524,16.508,50.132,18.025,47.481,18.025z"/></g></svg> \ No newline at end of file diff --git a/frontend/static/img/pacman.svg b/frontend/static/img/pacman.svg new file mode 100644 index 00000000..418c821d --- /dev/null +++ b/frontend/static/img/pacman.svg @@ -0,0 +1 @@ +<svg width="200px" height="200px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" class="lds-pacman" style="background: none;"><g style="display:block"><circle cx="76.8" cy="50" r="4" ng-attr-fill="{{config.c2}}" fill="black"><animate attributeName="cx" calcMode="linear" values="95;35" keyTimes="0;1" dur="1" begin="-0.67s" repeatCount="indefinite"></animate><animate attributeName="fill-opacity" calcMode="linear" values="0;1;1" keyTimes="0;0.2;1" dur="1" begin="-0.67s" repeatCount="indefinite"></animate></circle><circle cx="37.2" cy="50" r="4" ng-attr-fill="{{config.c2}}" fill="black"><animate attributeName="cx" calcMode="linear" values="95;35" keyTimes="0;1" dur="1" begin="-0.33s" repeatCount="indefinite"></animate><animate attributeName="fill-opacity" calcMode="linear" values="0;1;1" keyTimes="0;0.2;1" dur="1" begin="-0.33s" repeatCount="indefinite"></animate></circle><circle cx="57" cy="50" r="4" ng-attr-fill="{{config.c2}}" fill="black"><animate attributeName="cx" calcMode="linear" values="95;35" keyTimes="0;1" dur="1" begin="0s" repeatCount="indefinite"></animate><animate attributeName="fill-opacity" calcMode="linear" values="0;1;1" keyTimes="0;0.2;1" dur="1" begin="0s" repeatCount="indefinite"></animate></circle></g><g ng-attr-transform="translate({{config.showBeanOffset}} 0)" transform="translate(-15 0)"><path d="M50 50L20 50A30 30 0 0 0 80 50Z" ng-attr-fill="{{config.c1}}" fill="black" transform="rotate(33 50 50)"><animateTransform attributeName="transform" type="rotate" calcMode="linear" values="0 50 50;45 50 50;0 50 50" keyTimes="0;0.5;1" dur="1s" begin="0s" repeatCount="indefinite"></animateTransform></path><path d="M50 50L20 50A30 30 0 0 1 80 50Z" ng-attr-fill="{{config.c1}}" fill="black" transform="rotate(-33 50 50)"><animateTransform attributeName="transform" type="rotate" calcMode="linear" values="0 50 50;-45 50 50;0 50 50" keyTimes="0;0.5;1" dur="1s" begin="0s" repeatCount="indefinite"></animateTransform></path></g></svg> \ No newline at end of file diff --git a/rollup.config.js b/rollup.config.js index 1baf4d44..8d574f14 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -68,9 +68,10 @@ export default [ buildFrontEnd('frontend/src/archives/archives.ts'), buildFrontEnd('frontend/src/tracking/tracking.ts'), buildFrontEnd('frontend/src/status/status.ts'), + buildFrontEnd('frontend/src/workers/xc-score-worker.ts', true), ]; -function buildFrontEnd(input) { +function buildFrontEnd(input, worker) { return { input, @@ -95,7 +96,9 @@ function buildFrontEnd(input) { minifyHTML(), resolve(), cjs(), - typescript(), + typescript({ + tsconfigOverride: worker ? { compilerOptions: { lib: ['webworker'] } } : undefined + }), prod && terser({ output: { comments: false } }), ], };