├── .browserslistrc ├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── lib ├── perceptive-design.common.js ├── perceptive-design.common.js.map └── perceptive-design.css ├── package.json ├── public ├── .htaccess ├── assets │ ├── Beko-mochi.jpg │ ├── Dango.jpg │ ├── Mochi.jpg │ ├── icon_b.svg │ ├── icon_chain.svg │ ├── icon_cross.svg │ ├── icon_drag-handle.svg │ ├── icon_gear.svg │ ├── icon_h.svg │ ├── icon_hamburger.svg │ ├── icon_pause.svg │ ├── icon_play.svg │ ├── icon_plus.svg │ ├── icon_refresh.svg │ ├── icon_right-arrow.svg │ ├── icon_s.svg │ ├── icon_swap.svg │ ├── icon_unlinked-chain.svg │ ├── page-link_abstract.png │ ├── page-link_components-list.png │ ├── page-link_hardware-assign.png │ ├── page-link_parameter-control.png │ ├── page-link_pentool.png │ ├── page-link_transformation-matrix.png │ └── page-link_velocity-curve.png └── index.html ├── src ├── App.vue ├── assets │ ├── Mochi.jpg │ └── dice.svg ├── components │ ├── ConfigProvider.ts │ ├── InputAngle │ │ ├── InputAngle.vue │ │ └── index.ts │ ├── InputButton │ │ ├── InputButton.vue │ │ └── index.ts │ ├── InputCheckbox │ │ ├── InputCheckbox.vue │ │ └── index.ts │ ├── InputCodeEditor │ │ ├── InputCodeEditor.vue │ │ └── index.ts │ ├── InputColor │ │ ├── InputColor.vue │ │ ├── InputColorElement.vue │ │ └── index.ts │ ├── InputColorButton │ │ ├── InputColorButton.vue │ │ └── index.ts │ ├── InputColorPicker │ │ ├── InputColorPicker.vue │ │ └── index.ts │ ├── InputDropdown │ │ ├── InputDropdown.vue │ │ └── index.ts │ ├── InputIconAction │ │ ├── InputIconAction.vue │ │ └── index.ts │ ├── InputIconButton │ │ ├── InputIconButton.vue │ │ └── index.ts │ ├── InputIconToggle │ │ ├── InputIconToggle.vue │ │ └── index.ts │ ├── InputMatrix │ │ ├── InputMatrix.vue │ │ └── index.ts │ ├── InputMode │ │ ├── InputMode.vue │ │ └── index.ts │ ├── InputNumber │ │ ├── InputNumber.vue │ │ └── index.ts │ ├── InputPoint │ │ ├── InputPoint.vue │ │ └── index.ts │ ├── InputRange │ │ ├── InputRange.vue │ │ └── index.ts │ ├── InputSlider │ │ ├── InputSlider.vue │ │ └── index.ts │ ├── InputString │ │ ├── InputString.vue │ │ └── index.ts │ ├── InputTime │ │ ├── InputTime.vue │ │ └── index.ts │ ├── InputVector │ │ ├── InputVector.vue │ │ └── index.ts │ ├── LayoutSplitter │ │ ├── LayoutSplitter.vue │ │ └── index.ts │ ├── LayoutTab │ │ ├── LayoutTab.vue │ │ └── index.ts │ ├── LayoutTabs │ │ ├── LayoutTabs.vue │ │ └── index.ts │ ├── PaneBind │ │ ├── PaneBind.vue │ │ └── index.ts │ ├── ParamFieldAffine.vue │ ├── ParamFieldAngle.vue │ ├── ParamFieldBind.vue │ ├── ParamFieldCheckbox.vue │ ├── ParamFieldColor.vue │ ├── ParamFieldDropdown.vue │ ├── ParamFieldMode.vue │ ├── ParamFieldNumber.vue │ ├── ParamFieldPoint.vue │ ├── ParamFieldRange.vue │ ├── ParamFieldScale.vue │ ├── ParamFieldSeed.vue │ ├── ParamFieldSlider.vue │ ├── ParamFieldString.vue │ ├── ParamFieldVector.vue │ ├── ParameterList │ │ ├── Parameter.vue │ │ ├── ParameterList.vue │ │ └── index.ts │ ├── SelectionManager.vue │ ├── Timeline │ │ ├── Timeline.vue │ │ ├── TimelineSeekbarScale.vue │ │ └── index.ts │ ├── TimelineColor │ │ ├── TimelineColor.vue │ │ └── index.ts │ ├── TimelineDraw │ │ ├── TimelineDraw.vue │ │ └── index.ts │ ├── common │ │ ├── Drag.ts │ │ ├── GradientPalette │ │ │ ├── GradientPalette.vue │ │ │ ├── gradient-palette.frag │ │ │ ├── gradient-palette.vert │ │ │ └── index.ts │ │ ├── Icon.vue │ │ ├── Menu │ │ │ ├── Menu.vue │ │ │ ├── MenuItem.ts │ │ │ ├── Submenu.vue │ │ │ └── index.ts │ │ ├── Modal.vue │ │ ├── Popover.vue │ │ ├── Portal.ts │ │ ├── SvgArcArrow.vue │ │ ├── SvgArrow.vue │ │ └── SvgOverlayHorizontalDrag.vue │ └── index.ts ├── core │ ├── config.ts │ └── index.ts ├── data │ ├── BaseNode.ts │ ├── Color.ts │ ├── IndexedNode.ts │ ├── Mat2.ts │ ├── Mat2d.ts │ ├── Mat3.ts │ ├── Mat4.ts │ ├── Schema.ts │ ├── Time.ts │ ├── Vec2.ts │ ├── Vec3.ts │ ├── common.ts │ └── index.ts ├── index.ts ├── main.ts ├── manager │ ├── BindManager.ts │ └── HistoryManager.ts ├── math │ └── index.ts ├── pages │ ├── Abstract.vue │ ├── ComponentsList.vue │ ├── Easing │ │ ├── Easing.vue │ │ └── index.ts │ ├── GraphicsOnLisp │ │ ├── GraphicsOnLisp.vue │ │ ├── index.ts │ │ └── parser.ts │ ├── Home │ │ ├── Home.vue │ │ ├── PageLink.vue │ │ └── index.ts │ ├── ParameterControl │ │ ├── ParameterControl.vue │ │ ├── index.ts │ │ ├── polar-disp.frag │ │ └── polar-disp.meta.txt │ ├── Settings.vue │ └── TransformationMatrix │ │ ├── Param.vue │ │ ├── TransformationMatrix.vue │ │ ├── index.ts │ │ ├── matrix-util.ts │ │ ├── transform-stack-store.ts │ │ └── transform-store.ts ├── router.ts ├── shims-modules.d.ts ├── shims-tsx.d.ts ├── shims-vue.d.ts ├── style │ ├── article.styl │ ├── common.styl │ └── config.styl └── util │ ├── MouseDragEvent.ts │ ├── RoteryDrag.ts │ ├── Timecode.ts │ ├── constrain-value.ts │ ├── deepcopy.ts │ ├── deserialize.ts │ ├── disable-reactive.ts │ ├── force-notify.ts │ ├── index.ts │ └── split-to-parent-and-key.ts ├── tsconfig.json ├── tslint.json ├── typings ├── components │ ├── ConfigProvider.d.ts │ ├── InputAngle.vue.d.ts │ ├── InputButton.vue.d.ts │ ├── InputCheckbox.vue.d.ts │ ├── InputCodeEditor │ │ ├── InputCodeEditor.vue.d.ts │ │ └── index.d.ts │ ├── InputColor │ │ ├── InputColor.vue.d.ts │ │ ├── InputColorElement.vue.d.ts │ │ └── index.d.ts │ ├── InputColorButton.vue.d.ts │ ├── InputColorPicker.vue.d.ts │ ├── InputDropdown.vue.d.ts │ ├── InputIconAction │ │ ├── InputIconAction.vue.d.ts │ │ └── index.d.ts │ ├── InputIconButton.vue.d.ts │ ├── InputIconToggle.vue.d.ts │ ├── InputMatrix.vue.d.ts │ ├── InputMode.vue.d.ts │ ├── InputNumber.vue.d.ts │ ├── InputPoint.vue.d.ts │ ├── InputRange.vue.d.ts │ ├── InputSlider.vue.d.ts │ ├── InputString.vue.d.ts │ ├── InputTime │ │ ├── InputTime.vue.d.ts │ │ └── index.d.ts │ ├── InputVector.vue.d.ts │ ├── ParamFieldAffine.vue.d.ts │ ├── ParamFieldAngle.vue.d.ts │ ├── ParamFieldColor.vue.d.ts │ ├── ParamFieldNumber.vue.d.ts │ ├── ParamFieldPoint.vue.d.ts │ ├── ParamFieldRange.vue.d.ts │ ├── ParamFieldScale.vue.d.ts │ ├── ParamFieldSeed.vue.d.ts │ ├── ParamFieldSlider.vue.d.ts │ ├── Parameter.vue.d.ts │ ├── SelectionManager.vue.d.ts │ ├── Timeline │ │ ├── Timeline.vue.d.ts │ │ ├── TimelineSeekbarScale.vue.d.ts │ │ └── index.d.ts │ ├── TimelineColor.vue.d.ts │ ├── common │ │ ├── ButtonWrapper.d.ts │ │ ├── Drag.d.ts │ │ ├── GradientPalette │ │ │ ├── GradientPalette.vue.d.ts │ │ │ └── index.d.ts │ │ ├── Icon.vue.d.ts │ │ ├── Menu │ │ │ ├── Menu.vue.d.ts │ │ │ ├── MenuItem.d.ts │ │ │ ├── Submenu.vue.d.ts │ │ │ └── index.d.ts │ │ ├── Popover.vue.d.ts │ │ ├── Portal.d.ts │ │ ├── SvgArcArrow.vue.d.ts │ │ └── SvgArrow.vue.d.ts │ └── index.d.ts ├── data │ └── index.d.ts ├── index.d.ts ├── math │ └── index.d.ts └── util │ ├── RoteryDrag.d.ts │ ├── Timecode.d.ts │ ├── index.d.ts │ └── keypressed.d.ts ├── vue.config.js └── yarn.lock /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not ie <= 8 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | /resources 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw* 23 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "bracketSpacing": false, 5 | "semi": false 6 | } 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Baku Hashimoto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UI Study 2 | 3 | ![](./public/assets/page-link_parameter-control.png) 4 | 5 | **[Demo](http://ui.baku89.com)** 6 | 7 | Study of patameter controls UI for creative-purpose softwares. (Such as AfterEffects, Cinema4D) 8 | 9 | The motivation of this project is described here: [Study of UI](http://baku89.com/ui-study) 10 | 11 | ## Development Environment 12 | 13 | ### Transpiler languages 14 | 15 | - JavaScript: [TypeScript](https://www.typescriptlang.org/) 16 | - CSS: [Stylus](http://stylus-lang.com/) 17 | 18 | ### Build tools 19 | 20 | - Yarn 21 | - Vue CLI 3 22 | 23 | ### To transpile and debug: 24 | 25 | ``` 26 | yarn install 27 | yarn serve 28 | ``` 29 | 30 | ## Relating Documents 31 | 32 | - [Classified Table of Parameter Controls](https://docs.google.com/spreadsheets/d/1iyjMUTgJAZhPu4Rg2aV1QPgwSUWBAfuuRCwx0Yki4XM/edit#gid=0) 33 | 34 | ## License 35 | 36 | This repository is published under a MIT License. See the included [LISENCE file](/LICENSE). 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ui-study", 3 | "version": "2.0.3", 4 | "author": { 5 | "name": "Baku Hashimoto", 6 | "email": "mail@baku89.com" 7 | }, 8 | "types": "typings/index.d.ts", 9 | "private": true, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/baku89/ui-study.git" 13 | }, 14 | "files": [ 15 | "lib" 16 | ], 17 | "main": "lib/perceptive-design.common.js", 18 | "scripts": { 19 | "serve": "vue-cli-service serve --modern", 20 | "build": "vue-cli-service build --modern", 21 | "lint": "vue-cli-service lint", 22 | "build:lib": "vue-cli-service build --modern --target lib --formats commonjs --name perceptive-design ./src/index.ts --dest lib; rm -rf ./typings/router.d.ts ./typings/main.d.ts ./typings/pages" 23 | }, 24 | "dependencies": {}, 25 | "devDependencies": { 26 | "@types/color-convert": "^1.9.0", 27 | "@types/gl-matrix": "^2.4.5", 28 | "@types/hex-rgb": "^3.0.0", 29 | "@types/json5": "^0.0.30", 30 | "@types/mathjs": "^5.0.1", 31 | "@types/mousetrap": "^1.6.2", 32 | "@types/raf": "^3.4.0", 33 | "@types/webgl2": "^0.0.4", 34 | "@vue/cli-plugin-typescript": "^3.3.0", 35 | "@vue/cli-service": "^3.3.0", 36 | "autoprefixer-stylus": "^0.14.0", 37 | "brace": "^0.11.1", 38 | "case": "^1.6.1", 39 | "change-case": "^3.1.0", 40 | "color-convert": "^2.0.0", 41 | "core-decorators": "^0.20.0", 42 | "declaration-bundler-webpack-plugin": "^1.0.3", 43 | "deepcopy": "^2.0.0", 44 | "deepmerge": "^3.2.0", 45 | "eventemitter3": "^3.1.0", 46 | "fuzzy": "^0.1.3", 47 | "gl-matrix": "^3.0.0", 48 | "glslify-loader": "^2.0.0", 49 | "hex-rgb": "^4.0.0", 50 | "interactive-shader-format": "^2.9.0", 51 | "json-formatter-js": "^2.2.1", 52 | "json5": "^2.1.0", 53 | "keycode": "^2.2.0", 54 | "mathjs": "^5.9.0", 55 | "mobx": "^5.9.4", 56 | "mobx-vue": "^2.0.8", 57 | "mouse-event": "^1.0.5", 58 | "mousetrap": "^1.6.3", 59 | "nib": "^1.1.2", 60 | "popper.js": "^1.15.0", 61 | "raw-loader": "^1.0.0", 62 | "stylus": "^0.54.5", 63 | "stylus-loader": "^3.0.2", 64 | "tslint-config-prettier": "^1.17.0", 65 | "twgl.js": "^4.9.1", 66 | "typescript": "^3.0.0", 67 | "uid": "^0.0.2", 68 | "vue": "^2.6.10", 69 | "vue-class-component": "^7.0.2", 70 | "vue-property-decorator": "^8.1.0", 71 | "vue-router": "^3.0.3", 72 | "vue-template-compiler": "^2.6.10", 73 | "vuedraggable": "^2.20.0", 74 | "webmidi": "^2.3.3" 75 | }, 76 | "license": "MIT" 77 | } 78 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | RewriteEngine On 3 | RewriteBase / 4 | RewriteRule ^index\.html$ - [L] 5 | RewriteCond %{REQUEST_FILENAME} !-f 6 | RewriteCond %{REQUEST_FILENAME} !-d 7 | RewriteRule . /index.html [L] 8 | -------------------------------------------------------------------------------- /public/assets/Beko-mochi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baku89/ui-study/99799e504df0f74ee5838e81ee5258387df2a99e/public/assets/Beko-mochi.jpg -------------------------------------------------------------------------------- /public/assets/Dango.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baku89/ui-study/99799e504df0f74ee5838e81ee5258387df2a99e/public/assets/Dango.jpg -------------------------------------------------------------------------------- /public/assets/Mochi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baku89/ui-study/99799e504df0f74ee5838e81ee5258387df2a99e/public/assets/Mochi.jpg -------------------------------------------------------------------------------- /public/assets/icon_b.svg: -------------------------------------------------------------------------------- 1 | icon_bB -------------------------------------------------------------------------------- /public/assets/icon_chain.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /public/assets/icon_cross.svg: -------------------------------------------------------------------------------- 1 | icon_cross -------------------------------------------------------------------------------- /public/assets/icon_drag-handle.svg: -------------------------------------------------------------------------------- 1 | icon_drag-handle -------------------------------------------------------------------------------- /public/assets/icon_gear.svg: -------------------------------------------------------------------------------- 1 | icons -------------------------------------------------------------------------------- /public/assets/icon_h.svg: -------------------------------------------------------------------------------- 1 | icon_hH -------------------------------------------------------------------------------- /public/assets/icon_hamburger.svg: -------------------------------------------------------------------------------- 1 | icon_hamburger -------------------------------------------------------------------------------- /public/assets/icon_pause.svg: -------------------------------------------------------------------------------- 1 | icon_pause -------------------------------------------------------------------------------- /public/assets/icon_play.svg: -------------------------------------------------------------------------------- 1 | icon_play -------------------------------------------------------------------------------- /public/assets/icon_plus.svg: -------------------------------------------------------------------------------- 1 | icon_plus -------------------------------------------------------------------------------- /public/assets/icon_refresh.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /public/assets/icon_right-arrow.svg: -------------------------------------------------------------------------------- 1 | icon_right-arrow -------------------------------------------------------------------------------- /public/assets/icon_s.svg: -------------------------------------------------------------------------------- 1 | icon_sS -------------------------------------------------------------------------------- /public/assets/icon_swap.svg: -------------------------------------------------------------------------------- 1 | icon_exchange -------------------------------------------------------------------------------- /public/assets/icon_unlinked-chain.svg: -------------------------------------------------------------------------------- 1 | unlinked-chain -------------------------------------------------------------------------------- /public/assets/page-link_abstract.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baku89/ui-study/99799e504df0f74ee5838e81ee5258387df2a99e/public/assets/page-link_abstract.png -------------------------------------------------------------------------------- /public/assets/page-link_components-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baku89/ui-study/99799e504df0f74ee5838e81ee5258387df2a99e/public/assets/page-link_components-list.png -------------------------------------------------------------------------------- /public/assets/page-link_hardware-assign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baku89/ui-study/99799e504df0f74ee5838e81ee5258387df2a99e/public/assets/page-link_hardware-assign.png -------------------------------------------------------------------------------- /public/assets/page-link_parameter-control.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baku89/ui-study/99799e504df0f74ee5838e81ee5258387df2a99e/public/assets/page-link_parameter-control.png -------------------------------------------------------------------------------- /public/assets/page-link_pentool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baku89/ui-study/99799e504df0f74ee5838e81ee5258387df2a99e/public/assets/page-link_pentool.png -------------------------------------------------------------------------------- /public/assets/page-link_transformation-matrix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baku89/ui-study/99799e504df0f74ee5838e81ee5258387df2a99e/public/assets/page-link_transformation-matrix.png -------------------------------------------------------------------------------- /public/assets/page-link_velocity-curve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baku89/ui-study/99799e504df0f74ee5838e81ee5258387df2a99e/public/assets/page-link_velocity-curve.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | The UI Design for Design 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/assets/Mochi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baku89/ui-study/99799e504df0f74ee5838e81ee5258387df2a99e/src/assets/Mochi.jpg -------------------------------------------------------------------------------- /src/assets/dice.svg: -------------------------------------------------------------------------------- 1 | dice -------------------------------------------------------------------------------- /src/components/ConfigProvider.ts: -------------------------------------------------------------------------------- 1 | import {Component, Vue} from 'vue-property-decorator' 2 | import Case from 'case' 3 | 4 | import {ConfigDefault} from '../core/config' 5 | import Color from '../data/Color' 6 | import deepcopy from '../util/deepcopy' 7 | 8 | @Component({ 9 | data() { 10 | return { 11 | Config: deepcopy(ConfigDefault) 12 | } 13 | }, 14 | provide() { 15 | return { 16 | Config: this.$data.Config 17 | } 18 | } 19 | }) 20 | export default class ConfigProvider extends Vue { 21 | private Config!: any 22 | 23 | private get attrElement(): HTMLElement { 24 | return document.documentElement 25 | } 26 | 27 | private created() { 28 | this.$watch('Config.lang', this.updateLang, {immediate: true}) 29 | } 30 | 31 | private mounted() { 32 | // Set theme 33 | const {theme} = this.Config 34 | for (const key of Object.keys(theme)) { 35 | this.$watch( 36 | `Config.theme.${key}`, 37 | (newValue: any) => { 38 | this.updateThemeProperty(key, newValue) 39 | }, 40 | {immediate: true} 41 | ) 42 | } 43 | } 44 | 45 | private render() { 46 | return this.$scopedSlots.default!({ 47 | Config: this.Config 48 | }) 49 | } 50 | 51 | private updateLang(lang: string = this.Config.lang) { 52 | this.attrElement.classList.remove('en', 'ja') 53 | this.attrElement.classList.add(lang) 54 | } 55 | 56 | private updateThemeProperty(key: string, value: any) { 57 | const variableName = `--${Case.kebab(key)}` 58 | let cssValue = value 59 | 60 | if (/^color/.test(key)) { 61 | cssValue = (value as Color).cssColor 62 | } else if (/^layout/.test(key)) { 63 | cssValue += 'em' 64 | } else if (key === 'fontSize') { 65 | cssValue += 'px' 66 | } 67 | this.attrElement.style.setProperty(variableName, cssValue) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/components/InputAngle/InputAngle.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 106 | 107 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /src/components/InputAngle/index.ts: -------------------------------------------------------------------------------- 1 | import InputAngle from './InputAngle.vue' 2 | 3 | export default InputAngle 4 | -------------------------------------------------------------------------------- /src/components/InputButton/InputButton.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 31 | 32 | 33 | 68 | -------------------------------------------------------------------------------- /src/components/InputButton/index.ts: -------------------------------------------------------------------------------- 1 | import InputButton from './InputButton.vue' 2 | 3 | export default InputButton 4 | -------------------------------------------------------------------------------- /src/components/InputCheckbox/InputCheckbox.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 26 | 27 | 28 | 72 | 73 | -------------------------------------------------------------------------------- /src/components/InputCheckbox/index.ts: -------------------------------------------------------------------------------- 1 | import InputCheckbox from './InputCheckbox.vue' 2 | 3 | export default InputCheckbox 4 | -------------------------------------------------------------------------------- /src/components/InputCodeEditor/InputCodeEditor.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 62 | 63 | 79 | -------------------------------------------------------------------------------- /src/components/InputCodeEditor/index.ts: -------------------------------------------------------------------------------- 1 | import InputCodeEditor from './InputCodeEditor.vue' 2 | 3 | export default InputCodeEditor 4 | -------------------------------------------------------------------------------- /src/components/InputColor/InputColor.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 80 | 81 | 86 | -------------------------------------------------------------------------------- /src/components/InputColor/index.ts: -------------------------------------------------------------------------------- 1 | import InputColor from './InputColor.vue' 2 | 3 | export default InputColor 4 | -------------------------------------------------------------------------------- /src/components/InputColorButton/InputColorButton.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 95 | 96 | 97 | 144 | 145 | -------------------------------------------------------------------------------- /src/components/InputColorButton/index.ts: -------------------------------------------------------------------------------- 1 | import InputColorButton from './InputColorButton.vue' 2 | 3 | export default InputColorButton 4 | -------------------------------------------------------------------------------- /src/components/InputColorPicker/index.ts: -------------------------------------------------------------------------------- 1 | import InputColorPicker from './InputColorPicker.vue' 2 | 3 | export default InputColorPicker 4 | -------------------------------------------------------------------------------- /src/components/InputDropdown/InputDropdown.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 41 | 42 | 43 | 98 | 99 | -------------------------------------------------------------------------------- /src/components/InputDropdown/index.ts: -------------------------------------------------------------------------------- 1 | import InputDropdown from './InputDropdown.vue' 2 | 3 | export default InputDropdown 4 | -------------------------------------------------------------------------------- /src/components/InputIconAction/InputIconAction.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 40 | 41 | 42 | 63 | 64 | -------------------------------------------------------------------------------- /src/components/InputIconAction/index.ts: -------------------------------------------------------------------------------- 1 | import InputIconAction from './InputIconAction.vue' 2 | 3 | export default InputIconAction 4 | -------------------------------------------------------------------------------- /src/components/InputIconButton/InputIconButton.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 25 | 26 | 27 | 63 | 64 | -------------------------------------------------------------------------------- /src/components/InputIconButton/index.ts: -------------------------------------------------------------------------------- 1 | import InputIconButton from './InputIconButton.vue' 2 | 3 | export default InputIconButton 4 | -------------------------------------------------------------------------------- /src/components/InputIconToggle/InputIconToggle.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 34 | 35 | 36 | 69 | 70 | -------------------------------------------------------------------------------- /src/components/InputIconToggle/index.ts: -------------------------------------------------------------------------------- 1 | import InputIconToggle from './InputIconToggle.vue' 2 | 3 | export default InputIconToggle 4 | -------------------------------------------------------------------------------- /src/components/InputMatrix/InputMatrix.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 91 | 92 | 105 | -------------------------------------------------------------------------------- /src/components/InputMatrix/index.ts: -------------------------------------------------------------------------------- 1 | import InputMatrix from './InputMatrix.vue' 2 | 3 | export default InputMatrix 4 | -------------------------------------------------------------------------------- /src/components/InputMode/InputMode.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 35 | 36 | 91 | 92 | -------------------------------------------------------------------------------- /src/components/InputMode/index.ts: -------------------------------------------------------------------------------- 1 | import InputMode from './InputMode.vue' 2 | 3 | export default InputMode 4 | -------------------------------------------------------------------------------- /src/components/InputNumber/index.ts: -------------------------------------------------------------------------------- 1 | import InputNumber from './InputNumber.vue' 2 | 3 | export default InputNumber 4 | -------------------------------------------------------------------------------- /src/components/InputPoint/index.ts: -------------------------------------------------------------------------------- 1 | import InputPoint from './InputPoint.vue' 2 | 3 | export default InputPoint 4 | -------------------------------------------------------------------------------- /src/components/InputRange/index.ts: -------------------------------------------------------------------------------- 1 | import InputRange from './InputRange.vue' 2 | 3 | export default InputRange 4 | -------------------------------------------------------------------------------- /src/components/InputSlider/index.ts: -------------------------------------------------------------------------------- 1 | import InputSlider from './InputSlider.vue' 2 | 3 | export default InputSlider 4 | -------------------------------------------------------------------------------- /src/components/InputString/InputString.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 42 | 43 | 44 | 60 | 61 | -------------------------------------------------------------------------------- /src/components/InputString/index.ts: -------------------------------------------------------------------------------- 1 | import InputString from './InputString.vue' 2 | 3 | export default InputString 4 | -------------------------------------------------------------------------------- /src/components/InputTime/index.ts: -------------------------------------------------------------------------------- 1 | import InputTime from './InputTime.vue' 2 | 3 | export default InputTime 4 | -------------------------------------------------------------------------------- /src/components/InputVector/InputVector.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 61 | 62 | 66 | -------------------------------------------------------------------------------- /src/components/InputVector/index.ts: -------------------------------------------------------------------------------- 1 | import InputVector from './InputVector.vue' 2 | 3 | export default InputVector 4 | -------------------------------------------------------------------------------- /src/components/LayoutSplitter/index.ts: -------------------------------------------------------------------------------- 1 | import LayoutSplitter from './LayoutSplitter.vue' 2 | 3 | export default LayoutSplitter 4 | -------------------------------------------------------------------------------- /src/components/LayoutTab/LayoutTab.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 30 | 31 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/components/LayoutTab/index.ts: -------------------------------------------------------------------------------- 1 | import LayoutTab from './LayoutTab.vue' 2 | 3 | export default LayoutTab 4 | -------------------------------------------------------------------------------- /src/components/LayoutTabs/LayoutTabs.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 68 | 69 | 70 | 121 | 122 | -------------------------------------------------------------------------------- /src/components/LayoutTabs/index.ts: -------------------------------------------------------------------------------- 1 | import LayoutTabs from './LayoutTabs.vue' 2 | 3 | export default LayoutTabs 4 | -------------------------------------------------------------------------------- /src/components/PaneBind/index.ts: -------------------------------------------------------------------------------- 1 | import PaneBind from './PaneBind.vue' 2 | export default PaneBind 3 | -------------------------------------------------------------------------------- /src/components/ParamFieldAffine.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 35 | 36 | 37 | 40 | -------------------------------------------------------------------------------- /src/components/ParamFieldAngle.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 34 | 35 | 36 | 42 | -------------------------------------------------------------------------------- /src/components/ParamFieldBind.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /src/components/ParamFieldCheckbox.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/ParamFieldColor.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 28 | 29 | 30 | 43 | -------------------------------------------------------------------------------- /src/components/ParamFieldDropdown.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/components/ParamFieldMode.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/ParamFieldNumber.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/ParamFieldPoint.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 46 | 47 | 48 | 54 | -------------------------------------------------------------------------------- /src/components/ParamFieldRange.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 47 | 48 | 49 | 58 | -------------------------------------------------------------------------------- /src/components/ParamFieldScale.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 121 | 122 | 123 | 129 | -------------------------------------------------------------------------------- /src/components/ParamFieldSeed.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 52 | 53 | 54 | 60 | -------------------------------------------------------------------------------- /src/components/ParamFieldSlider.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 45 | 46 | 47 | 56 | -------------------------------------------------------------------------------- /src/components/ParamFieldString.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/ParamFieldVector.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/ParameterList/Parameter.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 85 | 86 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /src/components/ParameterList/index.ts: -------------------------------------------------------------------------------- 1 | import ParameterList from './ParameterList.vue' 2 | 3 | export default ParameterList 4 | -------------------------------------------------------------------------------- /src/components/Timeline/TimelineSeekbarScale.vue: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 62 | 63 | 64 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /src/components/Timeline/index.ts: -------------------------------------------------------------------------------- 1 | import Timeline from './Timeline.vue' 2 | 3 | export default Timeline 4 | -------------------------------------------------------------------------------- /src/components/TimelineColor/TimelineColor.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 60 | 61 | 62 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/components/TimelineColor/index.ts: -------------------------------------------------------------------------------- 1 | import TimelineColor from './TimelineColor.vue' 2 | 3 | export default TimelineColor 4 | -------------------------------------------------------------------------------- /src/components/TimelineDraw/TimelineDraw.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 85 | 86 | 87 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /src/components/TimelineDraw/index.ts: -------------------------------------------------------------------------------- 1 | import TimelineDraw from './TimelineDraw.vue' 2 | 3 | export default TimelineDraw 4 | -------------------------------------------------------------------------------- /src/components/common/GradientPalette/GradientPalette.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 153 | -------------------------------------------------------------------------------- /src/components/common/GradientPalette/gradient-palette.frag: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | #define MODE_HSL_H 10 4 | #define MODE_HSL_S 11 5 | #define MODE_HSL_L 12 6 | #define MODE_HSL_HS 13 7 | #define MODE_HSL_SL 14 8 | #define MODE_HSL_HL 15 9 | 10 | #define MODE_RGB_R 20 11 | #define MODE_RGB_G 21 12 | #define MODE_RGB_B 22 13 | #define MODE_RGB_RG 23 14 | #define MODE_RGB_GB 24 15 | #define MODE_RGB_BR 25 16 | 17 | #define MODE_HSV_H 30 18 | #define MODE_HSV_S 31 19 | #define MODE_HSV_V 32 20 | #define MODE_HSV_HS 33 21 | #define MODE_HSV_SV 34 22 | #define MODE_HSV_HV 35 23 | 24 | float hue2rgb(float f1, float f2, float hue) { 25 | if (hue < 0.0) 26 | hue += 1.0; 27 | else if (hue > 1.0) 28 | hue -= 1.0; 29 | float res; 30 | if ((6.0 * hue) < 1.0) 31 | res = f1 + (f2 - f1) * 6.0 * hue; 32 | else if ((2.0 * hue) < 1.0) 33 | res = f2; 34 | else if ((3.0 * hue) < 2.0) 35 | res = f1 + (f2 - f1) * ((2.0 / 3.0) - hue) * 6.0; 36 | else 37 | res = f1; 38 | return res; 39 | } 40 | 41 | vec3 hsl2rgb(vec3 hsl) { 42 | vec3 rgb; 43 | 44 | if (hsl.y == 0.0) { 45 | rgb = vec3(hsl.z); // Luminance 46 | } else { 47 | float f2; 48 | 49 | if (hsl.z < 0.5) 50 | f2 = hsl.z * (1.0 + hsl.y); 51 | else 52 | f2 = hsl.z + hsl.y - hsl.y * hsl.z; 53 | 54 | float f1 = 2.0 * hsl.z - f2; 55 | 56 | rgb.r = hue2rgb(f1, f2, hsl.x + (1.0/3.0)); 57 | rgb.g = hue2rgb(f1, f2, hsl.x); 58 | rgb.b = hue2rgb(f1, f2, hsl.x - (1.0/3.0)); 59 | } 60 | return rgb; 61 | } 62 | 63 | vec3 hsl2rgb(float h, float s, float l) { 64 | return hsl2rgb(vec3(h, s, l)); 65 | } 66 | 67 | vec3 hsv2rgb(vec3 c) { 68 | vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); 69 | vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); 70 | return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); 71 | } 72 | 73 | vec3 hsv2rgb(float h, float s, float v) { 74 | return hsv2rgb(vec3(h, s, v)); 75 | } 76 | 77 | uniform vec2 resolution; 78 | uniform int mode; 79 | uniform vec3 elements; 80 | 81 | void main() { 82 | vec2 uv = gl_FragCoord.xy / resolution; 83 | vec3 rgb = vec3(1.0, 0.0, 0.0); 84 | 85 | // HSL 86 | if (mode == MODE_HSL_H) { 87 | rgb = hsl2rgb(uv.y, elements.y / 100.0, elements.z / 100.0); 88 | } else if (mode == MODE_HSL_S) { 89 | rgb = hsl2rgb(elements.x / 360.0, uv.y, elements.z / 100.0); 90 | } else if (mode == MODE_HSL_L) { 91 | rgb = hsl2rgb(elements.x / 360.0, elements.y / 100.0, uv.y); 92 | } else if (mode == MODE_HSL_HS) { 93 | rgb = hsl2rgb(uv.x, uv.y, elements.z / 100.0); 94 | } else if (mode == MODE_HSL_SL) { 95 | rgb = hsl2rgb(elements.x / 360.0, uv.x, uv.y); 96 | } else if (mode == MODE_HSL_HL) { 97 | rgb = hsl2rgb(uv.x, elements.y / 100.0, uv.y); 98 | 99 | // RGB 100 | } else if (mode == MODE_RGB_R) { 101 | rgb = vec3(uv.y, elements.y / 255.0, elements.z / 255.0); 102 | } else if (mode == MODE_RGB_G) { 103 | rgb = vec3(elements.x / 255.0, uv.y, elements.z / 255.0); 104 | } else if (mode == MODE_RGB_B) { 105 | rgb = vec3(elements.x / 255.0, elements.y / 255.0, uv.y); 106 | } else if (mode == MODE_RGB_RG) { 107 | rgb = vec3(uv.x, uv.y, elements.z / 255.0); 108 | } else if (mode == MODE_RGB_GB) { 109 | rgb = vec3(elements.x / 255.0, uv.x, uv.y); 110 | } else if (mode == MODE_RGB_BR) { 111 | rgb = vec3(uv.y, elements.y / 255.0, uv.x); 112 | 113 | // HSV 114 | } else if (mode == MODE_HSV_H) { 115 | rgb = hsv2rgb(uv.y, elements.y / 100.0, elements.z / 100.0); 116 | } else if (mode == MODE_HSV_S) { 117 | rgb = hsv2rgb(elements.x / 360.0, uv.y, elements.z / 100.0); 118 | } else if (mode == MODE_HSV_V) { 119 | rgb = hsv2rgb(elements.x / 360.0, elements.y / 100.0, uv.y); 120 | } else if (mode == MODE_HSV_HS) { 121 | rgb = hsv2rgb(uv.x, uv.y, elements.z / 100.0); 122 | } else if (mode == MODE_HSV_SV) { 123 | rgb = hsv2rgb(elements.x / 360.0, uv.x, uv.y); 124 | } else if (mode == MODE_HSV_HV) { 125 | rgb = hsv2rgb(uv.x, elements.y / 100.0, uv.y); 126 | } 127 | 128 | gl_FragColor = vec4(rgb, 1.0); 129 | } -------------------------------------------------------------------------------- /src/components/common/GradientPalette/gradient-palette.vert: -------------------------------------------------------------------------------- 1 | attribute vec4 position; 2 | 3 | void main() { 4 | gl_Position = position; 5 | } -------------------------------------------------------------------------------- /src/components/common/GradientPalette/index.ts: -------------------------------------------------------------------------------- 1 | import GradientPalette from './GradientPalette.vue' 2 | 3 | export default GradientPalette 4 | -------------------------------------------------------------------------------- /src/components/common/Icon.vue: -------------------------------------------------------------------------------- 1 | 80 | 81 | 91 | 92 | -------------------------------------------------------------------------------- /src/components/common/Menu/Menu.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 130 | 131 | 152 | -------------------------------------------------------------------------------- /src/components/common/Menu/MenuItem.ts: -------------------------------------------------------------------------------- 1 | export default interface MenuItem { 2 | value: string | number | symbol 3 | label: string 4 | shortLabel?: string 5 | icon?: string 6 | type?: 'submenu' | 'compact' 7 | submenu?: MenuItem[] 8 | } 9 | -------------------------------------------------------------------------------- /src/components/common/Menu/index.ts: -------------------------------------------------------------------------------- 1 | import Menu from './Menu.vue' 2 | 3 | export default Menu 4 | -------------------------------------------------------------------------------- /src/components/common/Modal.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 27 | 28 | 55 | -------------------------------------------------------------------------------- /src/components/common/Popover.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 144 | 145 | 146 | 152 | 153 | -------------------------------------------------------------------------------- /src/components/common/Portal.ts: -------------------------------------------------------------------------------- 1 | import {Component, Vue} from 'vue-property-decorator' 2 | 3 | @Component({}) 4 | export default class Portal extends Vue { 5 | private originalParentEl!: (Node & ParentNode) | null 6 | 7 | private mounted() { 8 | if (!this.originalParentEl) { 9 | this.originalParentEl = this.$el.parentNode 10 | this.$emit('initial-parent', this.$el.parentNode) 11 | } 12 | 13 | this.changeParentEl(this.$root.$el) 14 | } 15 | 16 | private beforeDestroy() { 17 | this.killGhostElement(this.$el) 18 | } 19 | 20 | private render() { 21 | const defaultSlot = this.$slots.default 22 | 23 | if (defaultSlot && defaultSlot[0]) { 24 | return defaultSlot[0] 25 | } 26 | } 27 | 28 | private killGhostElement(el: Element) { 29 | if (el.parentNode) { 30 | this.changeParentEl(this.originalParentEl) 31 | // @ts-ignore 32 | this.$options._parentElm = this.originalParentEl 33 | el.parentNode.removeChild(el) 34 | } 35 | } 36 | 37 | private changeParentEl(newTarget: (Node & ParentNode) | null) { 38 | newTarget!.appendChild(this.$el) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/components/common/SvgArcArrow.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 67 | -------------------------------------------------------------------------------- /src/components/common/SvgArrow.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 55 | -------------------------------------------------------------------------------- /src/components/common/SvgOverlayHorizontalDrag.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 44 | -------------------------------------------------------------------------------- /src/components/index.ts: -------------------------------------------------------------------------------- 1 | import ConfigProvider from './ConfigProvider' 2 | import InputAngle from './InputAngle' 3 | import InputButton from './InputButton' 4 | import InputCodeEditor from './InputCodeEditor' 5 | import InputCheckbox from './InputCheckbox' 6 | import InputColor from './InputColor' 7 | import InputColorButton from './InputColorButton' 8 | import InputDropdown from './InputDropdown' 9 | import InputIconAction from './InputIconAction' 10 | import InputIconButton from './InputIconButton' 11 | import InputIconToggle from './InputIconToggle' 12 | import InputMatrix from './InputMatrix' 13 | import InputMode from './InputMode' 14 | import InputNumber from './InputNumber' 15 | import InputPoint from './InputPoint' 16 | import InputRange from './InputRange' 17 | import InputSlider from './InputSlider' 18 | import InputString from './InputString' 19 | import InputTime from './InputTime' 20 | import InputVector from './InputVector' 21 | import LayoutSplitter from './LayoutSplitter' 22 | import LayoutTabs from './LayoutTabs' 23 | import LayoutTab from './LayoutTab' 24 | import ParameterList from './ParameterList' 25 | import ParamFieldAffine from './ParamFieldAffine.vue' 26 | import ParamFieldAngle from './ParamFieldAngle.vue' 27 | import ParamFieldCheckbox from './ParamFieldCheckbox.vue' 28 | import ParamFieldColor from './ParamFieldColor.vue' 29 | import ParamFieldDropdown from './ParamFieldDropdown.vue' 30 | import ParamFieldNumber from './ParamFieldNumber.vue' 31 | import ParamFieldMode from './ParamFieldMode.vue' 32 | import ParamFieldPoint from './ParamFieldPoint.vue' 33 | import ParamFieldRange from './ParamFieldRange.vue' 34 | import ParamFieldScale from './ParamFieldScale.vue' 35 | import ParamFieldSeed from './ParamFieldSeed.vue' 36 | import ParamFieldSlider from './ParamFieldSlider.vue' 37 | import ParamFieldString from './ParamFieldString.vue' 38 | import SelectionManager from './SelectionManager.vue' 39 | import Timeline from './Timeline' 40 | import TimelineColor from './TimelineColor' 41 | import TimelineDraw from './TimelineDraw' 42 | 43 | export default { 44 | ConfigProvider, 45 | InputAngle, 46 | InputButton, 47 | InputCodeEditor, 48 | InputCheckbox, 49 | InputColor, 50 | InputColorButton, 51 | InputDropdown, 52 | InputIconAction, 53 | InputIconButton, 54 | InputIconToggle, 55 | InputMatrix, 56 | InputMode, 57 | InputNumber, 58 | InputPoint, 59 | InputRange, 60 | InputSlider, 61 | InputString, 62 | InputTime, 63 | InputVector, 64 | LayoutSplitter, 65 | LayoutTabs, 66 | LayoutTab, 67 | ParameterList, 68 | ParamFieldAffine, 69 | ParamFieldAngle, 70 | ParamFieldCheckbox, 71 | ParamFieldColor, 72 | ParamFieldDropdown, 73 | ParamFieldNumber, 74 | ParamFieldMode, 75 | ParamFieldPoint, 76 | ParamFieldRange, 77 | ParamFieldSeed, 78 | ParamFieldScale, 79 | ParamFieldSlider, 80 | ParamFieldString, 81 | SelectionManager, 82 | Timeline, 83 | TimelineColor, 84 | TimelineDraw 85 | } 86 | -------------------------------------------------------------------------------- /src/core/config.ts: -------------------------------------------------------------------------------- 1 | import Color from '../data/Color' 2 | import {p, g, SchemaGroup} from '../data/Schema' 3 | 4 | export const ConfigDefault = { 5 | lang: 'en', 6 | dragSpeed: 0.5, 7 | keySlower: '/key/alt', 8 | keyFaster: '/key/shift', 9 | keySymmetry: '/key/s', 10 | keyQuantize: '/key/q', 11 | keyScale: '/key/alt', 12 | quantizeAngles: [0, 45, 90, 135, 180, 225, 270, 315], 13 | theme: { 14 | fontSize: 12, 15 | fontMonospace: '"Roboto Condensed", monospace, sans-serif', 16 | fontCode: '"Fira Code", monospace, sans-serif', 17 | fontNormal: '"Roboto", Helvetica, Arial, sans-serif', 18 | colorActive: new Color('hex', '#63acf9'), 19 | colorBorder: new Color('hex', '#e0e0e0'), 20 | colorBorderText: new Color('hex', '#bbbbbb'), 21 | colorControl: new Color('hex', '#bbbbbb'), 22 | colorControlText: new Color('hex', '#999999'), 23 | colorBg: new Color('hex', '#f0f0f0'), 24 | colorField: new Color('hex', '#f9f9f9'), 25 | colorText: new Color('hex', '#444444'), 26 | colorSeek: new Color('hex', '#ff3854'), 27 | colorMenuBg: new Color('hex', '#000000'), 28 | colorMenuText: new Color('hex', '#ffffff'), 29 | colorMenuField: new Color('hex', '#000000'), 30 | colorMenuBorder: new Color('hex', '#333333'), 31 | layoutParamHeight: 2.8, 32 | layoutParamField1w: 6, 33 | layoutParamFieldGapWidget: 0.5, 34 | layoutParamFieldGapBox: 0.3, 35 | layoutInputHeight: 2, 36 | layoutPopoverPadding: 0.5 37 | } 38 | } 39 | 40 | export const ConfigSchema: SchemaGroup = g('config', {}, [ 41 | p('lang', { 42 | ui: 'dropdown', 43 | label: 'Language', 44 | labels: ['English', '日本語'], 45 | values: ['en', 'ja'] 46 | }), 47 | p('dragSpeed', { 48 | ui: 'number', 49 | unit: '/px' 50 | }), 51 | p('keySlower', {ui: 'bind'}), 52 | p('keyFaster', {ui: 'bind'}), 53 | p('keySymmetry', {ui: 'bind'}), 54 | p('keyQuantize', {ui: 'bind'}), 55 | p('keyScale', {ui: 'bind'}), 56 | p('quantizeAngles', { 57 | ui: 'string', 58 | toField: (v: number[]) => v.join(', '), 59 | toData: (v: string) => JSON.parse(`[${v}]`) 60 | }), 61 | g('theme', {label: 'Theme'}, [ 62 | p('fontSize', { 63 | ui: 'number', 64 | label: 'Font Size', 65 | min: 7, 66 | max: 20, 67 | step: 1, 68 | unit: 'px' 69 | }), 70 | p('fontMonospace', { 71 | ui: 'string' 72 | }), 73 | p('fontCode', { 74 | ui: 'string' 75 | }), 76 | p('fontNormal', { 77 | ui: 'string' 78 | }), 79 | p('colorActive', {ui: 'color'}), 80 | p('colorBorder', {ui: 'color'}), 81 | p('colorBorderText', {ui: 'color'}), 82 | p('colorControl', {ui: 'color'}), 83 | p('colorControlText', {ui: 'color'}), 84 | p('colorBg', {ui: 'color'}), 85 | p('colorField', {ui: 'color'}), 86 | p('colorText', {ui: 'color'}), 87 | p('colorSeek', {ui: 'color'}), 88 | p('colorMenuBg', {ui: 'color'}), 89 | p('colorMenuText', {ui: 'color'}), 90 | p('colorMenuField', {ui: 'color'}), 91 | p('colorMenuBorder', {ui: 'color'}), 92 | p('layoutParamHeight', { 93 | ui: 'number', 94 | unit: 'em', 95 | min: 0, 96 | max: 20, 97 | precision: 1 98 | }), 99 | p('layoutParamField1w', { 100 | ui: 'number', 101 | unit: 'em', 102 | min: 0, 103 | max: 20, 104 | precision: 1 105 | }), 106 | p('layoutParamFieldGapWidget', { 107 | ui: 'number', 108 | unit: 'em', 109 | min: 0, 110 | max: 20, 111 | precision: 1 112 | }), 113 | p('layoutParamFieldGapBox', { 114 | ui: 'number', 115 | unit: 'em', 116 | min: 0, 117 | max: 20, 118 | precision: 1 119 | }), 120 | p('layoutInputHeight', { 121 | ui: 'number', 122 | unit: 'em', 123 | min: 0, 124 | max: 20, 125 | precision: 1 126 | }), 127 | p('layoutPopoverPadding', { 128 | ui: 'number', 129 | unit: 'em', 130 | min: 0, 131 | max: 20, 132 | precision: 1 133 | }) 134 | ]) 135 | ]) 136 | -------------------------------------------------------------------------------- /src/core/index.ts: -------------------------------------------------------------------------------- 1 | // no 2 | -------------------------------------------------------------------------------- /src/data/BaseNode.ts: -------------------------------------------------------------------------------- 1 | import deepcopy from '../util/deepcopy' 2 | 3 | export default class BaseNode { 4 | public name!: string | null 5 | public _parent!: BaseNode | null 6 | 7 | constructor( 8 | name: string | null = null, 9 | attrs: {[s: string]: any} | null = null 10 | ) { 11 | this.name = name 12 | 13 | Object.defineProperties(this, { 14 | _parent: { 15 | writable: true, 16 | enumerable: false 17 | } 18 | }) 19 | 20 | if (attrs) { 21 | this.setAttrs(attrs) 22 | } 23 | } 24 | 25 | public clone(): BaseNode { 26 | return new BaseNode(this.name, deepcopy(this.getAttrs())) 27 | } 28 | 29 | public getAttrs(): {[s: string]: any} { 30 | const attrs = Object.assign({}, this) 31 | delete attrs.name 32 | return attrs 33 | } 34 | 35 | public setAttrs(attrs: {[s: string]: any}) { 36 | const self = this as {[s: string]: any} 37 | for (const key of Object.keys(attrs)) { 38 | self[key] = attrs[key] 39 | } 40 | } 41 | 42 | public serialize() { 43 | const attrs = this.getAttrs() 44 | 45 | const obj: any[] = [''] 46 | 47 | const className = this.constructor.name 48 | if (className !== 'IndexedNode') { 49 | obj[0] += `:${className}` 50 | } 51 | 52 | if (this.name) { 53 | obj[0] += `#${this.name}` 54 | } 55 | 56 | if (Object.keys(attrs).length > 0) { 57 | obj.push(attrs) 58 | } 59 | 60 | return obj 61 | } 62 | 63 | public toJSON() { 64 | return this.serialize() 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/data/Color.ts: -------------------------------------------------------------------------------- 1 | import colorConvert from 'color-convert' 2 | import {mod, lerp, clamp} from '../math' 3 | 4 | export type ColorMode = 'rgb' | 'hsv' | 'hsl' | 'hex' 5 | export type ColorElements = string | number[] 6 | 7 | interface ColorModeInfoValue { 8 | max: number[] 9 | prefix: string[] 10 | unit: string[] 11 | } 12 | 13 | export const ColorModeInfo: {[s: string]: ColorModeInfoValue} = { 14 | hex: {max: [NaN], prefix: [''], unit: ['']}, 15 | rgb: {max: [255, 255, 255], prefix: ['R', 'G', 'B'], unit: ['', '', '']}, 16 | hsl: {max: [360, 100, 100], prefix: ['H', 'S', 'L'], unit: ['°', '%', '%']}, 17 | hsv: {max: [360, 100, 100], prefix: ['H', 'S', 'V'], unit: ['°', '%', '%']} 18 | } 19 | 20 | export default class Color { 21 | public static cssColor(color: Color): string { 22 | return color.cssColor 23 | } 24 | 25 | public static convertMode(color: Color, mode: ColorMode) { 26 | const newColor = color.clone() 27 | return newColor.convertMode(mode) 28 | } 29 | 30 | public static adjustHSB( 31 | color: Color, 32 | hueRotate: number, 33 | saturate: number, 34 | brightness: number 35 | ) { 36 | const newColor = color.clone() 37 | return newColor.adjustHSB(hueRotate, saturate, brightness) 38 | } 39 | 40 | public mode!: ColorMode 41 | 42 | public elements!: ColorElements 43 | 44 | private _cssColor!: {value: string; hash: string} 45 | 46 | constructor(mode: ColorMode, elements: ColorElements) { 47 | this.mode = mode 48 | this.elements = elements 49 | 50 | Object.defineProperty(this, '_cssColor', { 51 | enumerable: false, 52 | writable: true, 53 | configurable: false, 54 | value: {value: '', hash: ''} 55 | }) 56 | } 57 | 58 | public get cssColor(): string { 59 | const {mode, elements} = this 60 | 61 | const hash = mode + elements 62 | 63 | if (this._cssColor.hash === hash) { 64 | return this._cssColor.value 65 | } 66 | 67 | let cssColor: string 68 | 69 | if (mode === 'hex') { 70 | cssColor = elements as string 71 | } else { 72 | const [v0, v1, v2] = elements 73 | if (mode === 'rgb') { 74 | cssColor = `rgb(${v0}, ${v1}, ${v2})` 75 | } else if (mode === 'hsl') { 76 | cssColor = `hsl(${v0}, ${v1}%, ${v2}%)` 77 | } else { 78 | const [r, g, b]: number[] = colorConvert[mode].rgb(elements as [ 79 | number, 80 | number, 81 | number 82 | ]) 83 | cssColor = `rgb(${r}, ${g}, ${b})` 84 | } 85 | } 86 | 87 | this._cssColor.hash = hash 88 | this._cssColor.value = cssColor 89 | 90 | return cssColor 91 | } 92 | 93 | public clone(): Color { 94 | const elements = Array.isArray(this.elements) 95 | ? [...this.elements] 96 | : this.elements 97 | return new Color(this.mode, elements) 98 | } 99 | 100 | public convertMode(mode: ColorMode): Color { 101 | const {mode: originalMode, elements} = this 102 | 103 | if (originalMode === 'hex') { 104 | // @ts-ignore 105 | this.elements = colorConvert[originalMode][mode](elements) 106 | } else { 107 | // @ts-ignore 108 | this.elements = colorConvert[originalMode][mode].apply(null, elements) 109 | if (mode === 'hex') { 110 | this.elements = `#${this.elements}` 111 | } 112 | } 113 | 114 | this.mode = mode 115 | 116 | return this 117 | } 118 | 119 | public adjustHSB(hueRotate: number, saturate: number, brightness: number) { 120 | const {mode} = this 121 | const isHSB = mode === 'hsv' || mode === 'hsl' 122 | 123 | // Convert to HSV if the color is neither HSV nor HSL 124 | if (!isHSB) { 125 | this.convertMode('hsv') 126 | } 127 | 128 | const hsb = this.elements as number[] 129 | 130 | // Hue-rotate 131 | if (hueRotate % 360 !== 0) { 132 | hsb[0] = mod(hsb[0] + hueRotate, 360) 133 | } 134 | 135 | // Saturate / Brightness (%) 136 | for (let [i, inc] of [saturate, brightness].entries()) { 137 | i += 1 138 | if (inc !== 0) { 139 | if (inc < 0) { 140 | hsb[i] = lerp(0, hsb[i], clamp(inc / 100 + 1, 0, 1)) 141 | } else { 142 | hsb[i] = lerp(hsb[i], 100, clamp(inc / 100, 0, 1)) 143 | } 144 | } 145 | } 146 | 147 | // Convert to original color mode 148 | if (!isHSB) { 149 | this.convertMode(mode) 150 | } 151 | 152 | return this 153 | } 154 | 155 | private toJSON() { 156 | const {mode, elements} = this 157 | return [':Color', [this.mode, this.elements]] 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/data/IndexedNode.ts: -------------------------------------------------------------------------------- 1 | import {observable, reaction} from 'mobx' 2 | import BaseNode from './BaseNode' 3 | import deepcopy from '../util/deepcopy' 4 | 5 | export default class IndexedNode extends BaseNode { 6 | public children!: BaseNode[] 7 | public _namedChildren!: {[s: string]: BaseNode} 8 | 9 | constructor( 10 | name: string | null = null, 11 | attrs: {[s: string]: any} | null = null, 12 | children: BaseNode[] = [] 13 | ) { 14 | super(name, attrs) 15 | this.children = children 16 | 17 | Object.defineProperties(this, { 18 | _parent: { 19 | writable: true, 20 | enumerable: false 21 | }, 22 | _namedChildren: { 23 | enumerable: false, 24 | value: {} 25 | } 26 | }) 27 | 28 | for (const child of this.children) { 29 | child._parent = this 30 | if (child.name) { 31 | this._namedChildren[child.name] = child 32 | } 33 | } 34 | } 35 | 36 | public clone(): IndexedNode { 37 | return new IndexedNode( 38 | this.name, 39 | deepcopy(this.getAttrs()), 40 | deepcopy(this.children) 41 | ) 42 | } 43 | 44 | public getAttrs(): {[s: string]: any} { 45 | const attrs = super.getAttrs() 46 | delete attrs.children 47 | return attrs 48 | } 49 | 50 | public serialize() { 51 | const obj = super.serialize() 52 | 53 | if (this.children.length > 0) { 54 | obj.push(Array.from(this.children)) 55 | } 56 | 57 | return obj 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/data/Mat2.ts: -------------------------------------------------------------------------------- 1 | export default class Mat2 extends Array {} 2 | -------------------------------------------------------------------------------- /src/data/Mat3.ts: -------------------------------------------------------------------------------- 1 | export default class Mat3 extends Array {} 2 | -------------------------------------------------------------------------------- /src/data/Mat4.ts: -------------------------------------------------------------------------------- 1 | export default class Mat4 extends Array {} 2 | -------------------------------------------------------------------------------- /src/data/Schema.ts: -------------------------------------------------------------------------------- 1 | import deepmerge from 'deepmerge' 2 | import Case from 'case' 3 | 4 | import BaseNode from './BaseNode' 5 | import IndexedNode from './IndexedNode' 6 | import deepcopy from '../util/deepcopy' 7 | 8 | function mergeSchemaPair( 9 | a: Schema | SchemaGroup, 10 | b: Schema | SchemaGroup 11 | ): Schema | SchemaGroup { 12 | if (a instanceof Schema && b instanceof Schema) { 13 | // If both objects are instances of Schema, just merge the attribute. 14 | return new Schema(b.name || a.name, deepmerge(a.getAttrs(), b.getAttrs())) 15 | } else if (a instanceof SchemaGroup && b instanceof SchemaGroup) { 16 | // If both objects are instances of SchemaGroup, merge the two array of children. 17 | // The way of merge is as same as how two objects are merged when using Object.assign({}, a, b) 18 | // The order of proerties of A will be saved and the properties that only B has will be appended after them. 19 | const out = new SchemaGroup( 20 | b.name || a.name, 21 | deepmerge(a.getAttrs(), b.getAttrs()), 22 | deepcopy(a.children) 23 | ) 24 | 25 | for (const childOfB of b.children) { 26 | const {name} = childOfB 27 | const childOfA = out._namedChildren[name] 28 | 29 | if (childOfA) { 30 | // out.set(name, ) 31 | const index = out.children.indexOf(childOfA) 32 | const newChild = mergeSchemaPair(childOfA, childOfB) 33 | out.children.splice(index, 1, newChild) 34 | out._namedChildren[name] = newChild 35 | } else { 36 | const newChild = childOfB.clone() 37 | out.children.push(newChild) 38 | out._namedChildren[name] = newChild 39 | } 40 | } 41 | return out 42 | } else { 43 | // If one is Schema and the other is SchemaGroup, merge would fail so trhow the error. 44 | throw new Error('Cannot merge because the types of two schema do not match') 45 | } 46 | } 47 | 48 | export function mergeSchema(schemas: SchemaGroup[]): SchemaGroup { 49 | const [car, ...cdr] = schemas 50 | return cdr.reduce( 51 | (prev, current) => mergeSchemaPair(prev, current), 52 | car 53 | ) as SchemaGroup 54 | } 55 | 56 | export interface BindSchema { 57 | address: string 58 | method: string 59 | option: {[name: string]: any} 60 | } 61 | 62 | export interface ISchema { 63 | ui?: string 64 | label?: string 65 | prefix?: string 66 | unit?: string 67 | labels?: string[] 68 | values?: string[] 69 | precision?: number 70 | min?: number | number[] 71 | max?: number | number[] 72 | step?: number 73 | keepProportion?: boolean 74 | bindList?: BindSchema[] 75 | toField?: (v: any) => any 76 | toData?: (v: any) => any 77 | } 78 | 79 | export class Schema extends BaseNode implements ISchema { 80 | public name!: string 81 | 82 | public ui!: string 83 | public label?: string 84 | public prefix?: string 85 | public unit?: string 86 | public labels?: string[] 87 | public values?: string[] 88 | public precision?: number 89 | public min?: number | number[] 90 | public max?: number | number[] 91 | public step?: number 92 | public keepProportion?: boolean 93 | public bindList?: BindSchema[] 94 | public toField?: (v: any) => any 95 | public toData?: (v: any) => any 96 | 97 | constructor(name: string, schemaObj: ISchema) { 98 | super(name, schemaObj) 99 | 100 | if (!schemaObj.label) { 101 | this.label = Case.capital(name) 102 | } 103 | } 104 | 105 | public clone(): Schema { 106 | return super.clone() as Schema 107 | } 108 | } 109 | 110 | export class SchemaGroup extends IndexedNode { 111 | public name!: string 112 | public label!: string 113 | public children!: Array 114 | public _namedChildren!: {[s: string]: Schema | SchemaGroup} 115 | 116 | constructor( 117 | name: string, 118 | schemaObj: {label?: string}, 119 | children: Array 120 | ) { 121 | super(name, schemaObj, children) 122 | 123 | if (!schemaObj || !schemaObj.label) { 124 | this.label = Case.capital(name) 125 | } 126 | } 127 | 128 | public clone(): SchemaGroup { 129 | return super.clone() as SchemaGroup 130 | } 131 | } 132 | 133 | export const p = (name: string, schemaObj: ISchema) => 134 | new Schema(name, schemaObj) 135 | 136 | export const g = ( 137 | name: string, 138 | schemaObj: any, 139 | children: Array 140 | ) => new SchemaGroup(name, schemaObj, children) 141 | -------------------------------------------------------------------------------- /src/data/Time.ts: -------------------------------------------------------------------------------- 1 | export default class Time {} 2 | -------------------------------------------------------------------------------- /src/data/Vec3.ts: -------------------------------------------------------------------------------- 1 | export default class Vec3 { 2 | public x: number 3 | public y: number 4 | public z: number 5 | 6 | constructor(x: number = 0, y?: number, z?: number) { 7 | if (y === undefined || z === undefined) { 8 | z = y = x 9 | } 10 | this.x = x 11 | this.y = y 12 | this.z = z 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/data/common.ts: -------------------------------------------------------------------------------- 1 | export const EPSILON = 0.000001 2 | -------------------------------------------------------------------------------- /src/data/index.ts: -------------------------------------------------------------------------------- 1 | import Color from './Color' 2 | 3 | // Transform 4 | export type DataTransformType1D = 5 | | 'translateX' 6 | | 'translateY' 7 | | 'scaleX' 8 | | 'scaleY' 9 | | 'scaleUniform' 10 | | 'rotate' 11 | | 'skewX' 12 | | 'skewY' 13 | 14 | export type DataTransformType2D = 'translate' | 'scale' | 'skew' 15 | 16 | export type DataTransformTypeMatrix = 'matrix' 17 | 18 | export type DataTransformType = 19 | | DataTransformType1D 20 | | DataTransformType2D 21 | | DataTransformTypeMatrix 22 | 23 | export type DataTransformValue = number | number[] 24 | 25 | export const DataTransformType1DList = [ 26 | 'translateX', 27 | 'translateY', 28 | 'scaleX', 29 | 'scaleY', 30 | 'scaleUniform', 31 | 'rotate', 32 | 'skewX', 33 | 'skewY' 34 | ] 35 | 36 | export const DataTransformType2DList = ['translate', 'scale', 'skew'] 37 | 38 | export const DataTransformTypeMatrixList = ['matrix'] 39 | 40 | export const DataTransformTypeList = [ 41 | ...DataTransformType1DList, 42 | ...DataTransformType2DList, 43 | ...DataTransformTypeMatrixList 44 | ] 45 | 46 | export interface DataTransformStack { 47 | type: DataTransformType 48 | value: DataTransformValue 49 | active: boolean 50 | } 51 | 52 | export type DataTransform = DataTransformStack[] 53 | 54 | export interface Store { 55 | [name: string]: number | string | number[] | Color | Store 56 | } 57 | 58 | export {Color} 59 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import components from './components' 2 | import Drag from './components/common/Drag' 3 | import * as util from './util' 4 | import * as math from './math' 5 | 6 | const common = {Drag} 7 | 8 | export default {components, common, util, math} 9 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import {glMatrix} from 'gl-matrix' 4 | 5 | import router from './router' 6 | 7 | glMatrix.setMatrixArrayType(Array) 8 | 9 | Vue.config.productionTip = false 10 | 11 | new Vue({ 12 | router, 13 | render: h => h(App) 14 | }).$mount('#app') 15 | -------------------------------------------------------------------------------- /src/manager/HistoryManager.ts: -------------------------------------------------------------------------------- 1 | interface HistoryDescriptor { 2 | undo: () => void 3 | redo: () => void 4 | label?: string 5 | } 6 | 7 | class HistoryManager { 8 | private histories: HistoryDescriptor[] = [] 9 | private index: number = 0 10 | 11 | public addHistory(descriptor: HistoryDescriptor) { 12 | if (this.index >= 1) { 13 | this.histories.splice(0, this.index) 14 | this.index = 0 15 | } 16 | this.histories.unshift(descriptor) 17 | } 18 | 19 | public get canUndo(): boolean { 20 | return this.index < this.histories.length 21 | } 22 | 23 | public get canRedo(): boolean { 24 | return this.index >= 1 25 | } 26 | 27 | public undo() { 28 | if (!this.canUndo) { 29 | console.log('cannot undo') 30 | return 31 | } 32 | this.histories[this.index].undo() 33 | this.index++ 34 | } 35 | 36 | public redo() { 37 | if (!this.canRedo) { 38 | console.log('cannot redo') 39 | return 40 | } 41 | this.index-- 42 | this.histories[this.index].redo() 43 | } 44 | } 45 | 46 | export default new HistoryManager() 47 | -------------------------------------------------------------------------------- /src/math/index.ts: -------------------------------------------------------------------------------- 1 | function lerp(a: number, b: number, t: number) { 2 | return a + (b - a) * t 3 | } 4 | 5 | function fit( 6 | value: number, 7 | srcmin: number, 8 | srcmax: number, 9 | destmin: number, 10 | destmax: number 11 | ) { 12 | const t = (value - srcmin) / (srcmax - srcmin) 13 | return destmin + (destmax - destmin) * t 14 | } 15 | 16 | function clamp(value: number, min: number, max: number) { 17 | return min < max 18 | ? value < min 19 | ? min 20 | : value > max 21 | ? max 22 | : value 23 | : value < max 24 | ? max 25 | : value > min 26 | ? min 27 | : value 28 | } 29 | 30 | function mod(a: number, b: number): number { 31 | return ((a % b) + b) % b 32 | } 33 | 34 | function ratio( 35 | value: number, 36 | bottom: number, 37 | top: number, 38 | clamped: boolean = false 39 | ) { 40 | const t = (value - bottom) / (top - bottom) 41 | return clamped ? clamp(t, 0, 1) : t 42 | } 43 | 44 | const numberReg = /[+-]?([0-9]*[.])?[0-9]+/ 45 | 46 | function parseNumber(str: string): number { 47 | // if (numberReg.test(str)) { 48 | // return parseFloat(str) 49 | // } else { 50 | // return NaN 51 | // } 52 | return parseFloat(str) 53 | } 54 | 55 | function toFixed( 56 | value: number, 57 | precision: number, 58 | omitZeros: boolean = true 59 | ): string { 60 | const fixed = (+value).toFixed(precision) 61 | return fixed === '0' 62 | ? fixed 63 | : omitZeros 64 | ? fixed.replace(/^(.+\.[1-9]*)0+$/, '$1').replace(/\.$/, '') 65 | : fixed 66 | } 67 | 68 | function toRadians(degrees: number) { 69 | return (degrees * Math.PI) / 180 70 | } 71 | 72 | function toDegrees(radians: number) { 73 | return (radians * 180) / Math.PI 74 | } 75 | 76 | function cycleMod(value: number, inc: number, max: number) { 77 | inc = (inc % max) + max 78 | return (value + inc) % max 79 | } 80 | 81 | function quantize(value: number, step: number) { 82 | return Math.round(value / step) * step 83 | } 84 | 85 | function isInteger(value: number): boolean { 86 | return Math.floor(value) - value === 0 87 | } 88 | 89 | export { 90 | lerp, 91 | fit, 92 | clamp, 93 | parseNumber, 94 | toFixed, 95 | ratio, 96 | mod, 97 | toRadians, 98 | toDegrees, 99 | cycleMod, 100 | quantize, 101 | isInteger 102 | } 103 | -------------------------------------------------------------------------------- /src/pages/Abstract.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 25 | -------------------------------------------------------------------------------- /src/pages/Easing/Easing.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 49 | 50 | 53 | -------------------------------------------------------------------------------- /src/pages/Easing/index.ts: -------------------------------------------------------------------------------- 1 | import Easing from './Easing.vue' 2 | 3 | export default Easing 4 | -------------------------------------------------------------------------------- /src/pages/GraphicsOnLisp/index.ts: -------------------------------------------------------------------------------- 1 | import GraphicsOnLisp from './GraphicsOnLisp.vue' 2 | 3 | export default GraphicsOnLisp 4 | -------------------------------------------------------------------------------- /src/pages/GraphicsOnLisp/parser.ts: -------------------------------------------------------------------------------- 1 | // type Atom = number | string 2 | 3 | // type Node = Atom | Atom[] 4 | 5 | // export function atom(token: string): Atom { 6 | // const val = parseFloat(token) 7 | // return isNaN(val) ? token : val 8 | // } 9 | 10 | // export function tokenize(str: string): string[] { 11 | // return str 12 | // .replace(/(\(|\))/g, ' $1 ') 13 | // .replace(/\n/g, ' ') 14 | // .split(' ') 15 | // .filter(x => x !== '') 16 | // } 17 | 18 | // export function readFrom(tokens: string[]): any { 19 | // if (tokens.length === 0) { 20 | // throw Error('unexpected EOF while reading') 21 | // } 22 | 23 | // const token: string = tokens.shift() as string 24 | 25 | // if (token === '(') { 26 | // const L = [] 27 | // while (tokens[0] !== ')') { 28 | // L.push(readFrom(tokens)) 29 | // } 30 | // tokens.shift() 31 | // return L 32 | // } else if (token === ')') { 33 | // throw Error('unexpected )') 34 | // } else { 35 | // return atom(token) 36 | // } 37 | // } 38 | 39 | // export function createEnv(outer: any, names: string[], values: Atom[]) { 40 | // const dict = new Map() 41 | // names.forEach((p, i) => dict.set(p, values[i])) 42 | // return { 43 | // set: (key: string, val: any) => dict.set(key, val) && null, 44 | // get: (key: string) => (dict.has(key) ? dict.get(key) : outer), 45 | // upd: (key: string, val: any) => 46 | // dict.has(key) ? dict.set(key, val) && null : outer.upd(key, val) 47 | // } 48 | // } 49 | 50 | // export function globalEnv() { 51 | // const env = createEnv(null, [], []) 52 | // env.set('+', (xs: number[]) => 53 | // xs.reduce((acc: number, x: number) => acc + x, 0) 54 | // ) 55 | // } 56 | 57 | // function lEval(_x: any, _env: any) { 58 | // let [x, env] = [_x, _env] 59 | // for (;;) { 60 | // if (typeof x === 'string') { 61 | // // symbol 62 | // return env.get(x) 63 | // } else if (typeof x === 'number') { 64 | // // number 65 | // return x 66 | // } else if (x[0] === 'quote') { 67 | // return x.slice(1) 68 | // } else if (x[0] === 'if') { 69 | // const [, test, conseq, alt] = x 70 | // x = lEval(test, env) ? conseq : alt // tail call 71 | // } else if (x[0] === 'set!') { 72 | // const [, name, exp] = x 73 | // env.upd(name, lEval(exp, env)) 74 | // return 75 | // } else if (x[0] === 'define') { 76 | // const [, name, exp] = x 77 | // env.set(name, lEval(exp, env)) 78 | // return 79 | // } else if (x[0] === 'begin') { 80 | // x.slice(1, x.length - 1).map(ex => lEval(ex, env)) 81 | // x = x[x.length - 1] // tail call 82 | // } else if (x[0] === 'lambda') { 83 | // const [, params, exp] = x 84 | // return {env, isUDF: true, params, exp} 85 | // } else { 86 | // // apply function 87 | // const [op, ...args] = x.map(ex => lEval(ex, env)) 88 | // if (isProcedure(op)) { 89 | // // tail call 90 | // x = op.exp 91 | // env = createEnv(op.env, op.params, args) 92 | // } else { 93 | // return op(args) 94 | // } 95 | // } 96 | // } 97 | // } 98 | -------------------------------------------------------------------------------- /src/pages/Home/PageLink.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 55 | 56 | 117 | -------------------------------------------------------------------------------- /src/pages/Home/index.ts: -------------------------------------------------------------------------------- 1 | import Home from './Home.vue' 2 | 3 | export default Home 4 | -------------------------------------------------------------------------------- /src/pages/ParameterControl/index.ts: -------------------------------------------------------------------------------- 1 | import ParameterControl from './ParameterControl.vue' 2 | 3 | export default ParameterControl 4 | -------------------------------------------------------------------------------- /src/pages/ParameterControl/polar-disp.meta.txt: -------------------------------------------------------------------------------- 1 | { 2 | "CREDIT": "Baku Hashimoto", 3 | "CATEGORIES": ["BAKU"], 4 | "INPUTS": [ 5 | { 6 | "NAME": "intensity", 7 | "LABEL": "Intensity", 8 | "TYPE": "float", 9 | "DEFAULT": 20, 10 | "MIN": 0, 11 | "MAX": 100, 12 | "UNIT": "%" 13 | }, 14 | { 15 | "NAME": "iteration", 16 | "LABEL": "Iteration", 17 | "TYPE": "long", 18 | "DEFAULT": 2, 19 | "MIN": 1, 20 | "MAX": 5, 21 | "UI_TYPE": "integer" 22 | }, 23 | { 24 | "NAME": "scale", 25 | "LABEL": "Scale", 26 | "TYPE": "point2D", 27 | "UI_TYPE": "scale", 28 | "DEFAULT": [0.2, 0.2] 29 | }, 30 | { 31 | "NAME": "angle", 32 | "LABEL": "Angle", 33 | "TYPE": "float", 34 | "UI_TYPE": "angle" 35 | }, 36 | { 37 | "NAME": "offset", 38 | "LABEL": "Offset", 39 | "TYPE": "point2D", 40 | "UNIT": "%" 41 | }, 42 | { 43 | "NAME": "cropTop", 44 | "LABEL": "Crop Top", 45 | "TYPE": "float", 46 | "DEFAULT": 5, 47 | "MIN": 0, 48 | "MAX": 50, 49 | "UNIT": "%" 50 | }, 51 | { 52 | "NAME": "cropRight", 53 | "LABEL": "Crop Right", 54 | "TYPE": "float", 55 | "DEFAULT": 5, 56 | "MIN": 0, 57 | "MAX": 50, 58 | "UNIT": "%" 59 | }, 60 | { 61 | "NAME": "cropBottom", 62 | "LABEL": "Crop Bottom", 63 | "TYPE": "float", 64 | "DEFAULT": 5, 65 | "MIN": 0, 66 | "MAX": 50, 67 | "UNIT": "%" 68 | }, 69 | { 70 | "NAME": "cropLeft", 71 | "LABEL": "Crop Left", 72 | "TYPE": "float", 73 | "DEFAULT": 5, 74 | "MIN": 0, 75 | "MAX": 50, 76 | "UNIT": "%" 77 | }, 78 | { 79 | "NAME": "noiseType", 80 | "LABEL": "Noise Type", 81 | "TYPE": "long", 82 | "VALUES": [0, 1], 83 | "LABELS": ["Simplex", "Periodic"], 84 | "UI_TYPE": "mode" 85 | }, 86 | { 87 | "NAME": "frameColor", 88 | "LABEL": "Frame Color", 89 | "TYPE": "color" 90 | } 91 | ], 92 | "PASSES": [{}] 93 | } 94 | -------------------------------------------------------------------------------- /src/pages/Settings.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/pages/TransformationMatrix/Param.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 50 | 51 | 52 | 98 | -------------------------------------------------------------------------------- /src/pages/TransformationMatrix/index.ts: -------------------------------------------------------------------------------- 1 | import TransformationMatrix from './TransformationMatrix.vue' 2 | 3 | export default TransformationMatrix 4 | -------------------------------------------------------------------------------- /src/pages/TransformationMatrix/matrix-util.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baku89/ui-study/99799e504df0f74ee5838e81ee5258387df2a99e/src/pages/TransformationMatrix/matrix-util.ts -------------------------------------------------------------------------------- /src/pages/TransformationMatrix/transform-stack-store.ts: -------------------------------------------------------------------------------- 1 | import {observable, computed} from 'mobx' 2 | import {toRadians} from '../../math' 3 | import {mat2d} from 'gl-matrix' 4 | import { 5 | DataTransformValue, 6 | DataTransformStack, 7 | DataTransformType 8 | } from '../../data' 9 | 10 | export default class TransformStackStore { 11 | @observable public type!: DataTransformType 12 | @observable public value!: DataTransformValue 13 | @observable public active!: boolean 14 | 15 | constructor({type, value, active}: DataTransformStack) { 16 | this.type = type 17 | this.value = value 18 | this.active = active 19 | } 20 | 21 | @computed public get matrix(): mat2d { 22 | let type = this.type 23 | let value = this.value 24 | const out = mat2d.create() 25 | 26 | // normalize to 2d vector 27 | const isSingleDim = /(X|Y)$/.exec(type) 28 | if (isSingleDim) { 29 | type = type.substr(0, type.length - 1) as DataTransformType 30 | const ident = type.includes('scale') ? 1 : 0 31 | const newValue = [ident, ident] 32 | const dim = type.substr(-1) === 'X' ? 0 : 0 33 | newValue[dim] = value as number 34 | value = newValue 35 | } else if (type === 'scaleUniform') { 36 | type = 'scale' 37 | value = [value as number, value as number] 38 | } 39 | 40 | if (type === 'translate') { 41 | mat2d.fromTranslation(out, value as number[]) 42 | } else if (type === 'rotate') { 43 | mat2d.fromRotation(out, toRadians(value as number)) 44 | } else if (type === 'scale') { 45 | mat2d.fromScaling(out, value as number[]) 46 | } else if (type === 'skew') { 47 | const tanx = Math.tan(toRadians((value as number[])[0])) 48 | const tany = Math.tan(toRadians((value as number[])[1])) 49 | mat2d.set(out, 1, tany, tanx, 1, 0, 0) 50 | } else if (type === 'matrix') { 51 | mat2d.copy(out, value as any) 52 | } else { 53 | mat2d.identity(out) 54 | } 55 | return out 56 | } 57 | 58 | @computed public get matrixInverse(): mat2d | null { 59 | const out = mat2d.create() 60 | return mat2d.invert(out, this.matrix) === null ? null : out 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/router.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | 4 | import Abstract from './pages/Abstract.vue' 5 | import ComponentsList from './pages/ComponentsList.vue' 6 | import ParameterControl from './pages/ParameterControl' 7 | import TransformationMatrix from './pages/TransformationMatrix' 8 | import GraphicsOnLisp from './pages/GraphicsOnLisp' 9 | import Easing from './pages/Easing' 10 | 11 | Vue.use(Router) 12 | 13 | export default new Router({ 14 | mode: 'history', 15 | base: process.env.BASE_URL, 16 | routes: [ 17 | { 18 | path: '/abstract', 19 | name: 'abstract', 20 | component: Abstract 21 | }, 22 | { 23 | path: '/components-list', 24 | name: 'components-list', 25 | component: ComponentsList 26 | }, 27 | { 28 | path: '/parameter-control', 29 | name: 'parameter-control', 30 | component: ParameterControl 31 | }, 32 | { 33 | path: '/transformation-matrix', 34 | name: 'transformation-matrix', 35 | component: TransformationMatrix 36 | }, 37 | { 38 | path: '/graphics-on-lisp', 39 | name: 'graphics-on-lisp', 40 | component: GraphicsOnLisp 41 | }, 42 | { 43 | path: '/easing', 44 | name: 'easing', 45 | component: Easing 46 | } 47 | ] 48 | }) 49 | -------------------------------------------------------------------------------- /src/shims-modules.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'uid' { 2 | export default function(length: number): string 3 | } 4 | 5 | declare module 'mouse-event' { 6 | export function buttons(event: MouseEvent): number 7 | } 8 | 9 | declare module 'deepcopy' { 10 | export default function(value: any): any 11 | } 12 | -------------------------------------------------------------------------------- /src/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, {VNode} from 'vue' 2 | 3 | declare global { 4 | namespace JSX { 5 | // tslint:disable no-empty-interface 6 | interface Element extends VNode {} 7 | // tslint:disable no-empty-interface 8 | interface ElementClass extends Vue {} 9 | interface IntrinsicElements { 10 | [elem: string]: any 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue'; 3 | export default Vue; 4 | } 5 | -------------------------------------------------------------------------------- /src/style/article.styl: -------------------------------------------------------------------------------- 1 | ::selection 2 | background var(--color-border) 3 | 4 | .article 5 | margin 0 auto 6 | padding 3em 2em 2em 7 | max-width 45em 8 | font-size 1.5em 9 | font-family 'YakuHanMPs', 'YakuHanJPs', 'EB Garamond', Georgia, yu-mincho-pr6n, YuMincho, '游明朝', 'Yu Mincho', YuMincho, 'Hiragino Mincho ProN', serif 10 | 11 | section 12 | margin-bottom 2em 13 | 14 | em 15 | // text-decoration underline 16 | // margin 0 2px 17 | // box-shadow 0 0 0 1px var(--color-border), 0 0 0 2px white, 0 0 0 3px var(--color-border) 18 | // font-weight 500 19 | font-style italic 20 | 21 | h1, h2, h3, h4, h5, h6 22 | font-family Roboto, sans-serif 23 | 24 | h1 25 | margin-bottom 1em 26 | color transparent 27 | font-weight 500 28 | font-size 3em 29 | text-stroke 1px var(--color-text) 30 | -webkit-text-stroke 1px var(--color-text) 31 | 32 | h2 33 | margin-top 0.5em 34 | margin-bottom 0.7em 35 | // margin-bottom 1em 36 | font-weight 500 37 | font-size 1.2em 38 | 39 | p 40 | margin-bottom 1.5em 41 | line-height 1.6 42 | 43 | a 44 | color var(--color-active) 45 | 46 | &:hover, &:focus, &:active 47 | text-decoration underline 48 | 49 | [lang=en] 50 | display none 51 | 52 | .en & 53 | display block 54 | 55 | [lang=ja] 56 | display none 57 | font-size 0.95em 58 | line-height 1.8 59 | 60 | .ja & 61 | display block 62 | 63 | .outline 64 | display inline-block 65 | margin-bottom 0.1em 66 | color transparent 67 | text-stroke 1px var(--color-text) 68 | -webkit-text-stroke 1px var(--color-text) -------------------------------------------------------------------------------- /src/style/common.styl: -------------------------------------------------------------------------------- 1 | flex-version = flex 2 | 3 | @import 'nib' 4 | @import url('https://fonts.googleapis.com/css?family=Roboto+Condensed:400,700|Roboto:300,400,500') 5 | 6 | global-reset() 7 | 8 | @import './config.styl' 9 | 10 | // Variables 11 | :root 12 | --color-active #63acf9 13 | --color-border #e0e0e0 14 | --color-border-text #bbbbbb 15 | --color-control #bbbbbb 16 | --color-control-text #999999 17 | --color-bg #f0f0f0 18 | --color-field #f9f9f9 19 | --color-text #444444 20 | --color-parameter-hover #e4e4e4 21 | --color-seek #ff3854 22 | --font-monospace 'Roboto Condensed', monospace, sans-serif 23 | --font-code 'Fira Code', monospace, sans-serif 24 | --font-normal 'Roboto', Helvetica, Arial, sans-serif 25 | --color-menu-bg #000000 26 | --color-menu-text #ffffff 27 | --color-menu-field #000000 28 | --color-menu-border #333 29 | // Layout 30 | --layout-param-height 2.8em 31 | --layout-param-field-1w 6em 32 | --layout-param-field-gap-widget 0.5em 33 | --layout-param-field-gap-box 0.3em 34 | --layout-input-height 2em 35 | --layout-popover-padding 0.5em 36 | 37 | *, ::after, ::before 38 | box-sizing border-box 39 | outline none 40 | -webkit-tap-highlight-color transparent 41 | 42 | // Reset form elements 43 | input, button, select 44 | margin 0 45 | padding 0 46 | outline none 47 | border 0 48 | background transparent 49 | color inherit 50 | font-size inherit 51 | appearance none 52 | 53 | &::-webkit-outer-spin-button, &::-webkit-inner-spin-button 54 | margin 0 55 | -webkit-appearance none 56 | 57 | html 58 | font-size var(--font-size) 59 | 60 | .ui 61 | background var(--color-bg) 62 | font-size var(--font-size) 63 | font-family var(--font-normal) 64 | user-select none 65 | 66 | // Parameter 67 | .param-field 68 | 69 | &--1w, &--2w, &--3n, &--4n 70 | margin-right var(--layout-param-field-gap-widget) 71 | 72 | &--1w 73 | width var(--layout-param-field-1w) 74 | 75 | &--2w 76 | width 'calc(%s * 2)' % var(--layout-param-field-1w) 77 | 78 | &--3n 79 | width 'calc(%s / 1.5 * 3)' % var(--layout-param-field-1w) 80 | 81 | &--4n 82 | width 'calc(%s / 1.5 * 4)' % var(--layout-param-field-1w) 83 | 84 | // SVG 85 | .svg-overlay 86 | position fixed 87 | top 0 88 | left 0 89 | z-index 2001 90 | width 100vw 91 | height 100vh 92 | 93 | .svg-ui 94 | overflow visible 95 | 96 | .svg-overlay, .svg-ui 97 | svg 98 | position absolute 99 | top 0 100 | left 0 101 | width 100% 102 | height 100% 103 | 104 | .guide 105 | stroke var(--color-border) 106 | stroke-width 1px 107 | fill none 108 | 109 | .stroke 110 | stroke var(--color-active) 111 | stroke-width 3px 112 | fill none 113 | 114 | .dashed-stroke 115 | stroke var(--color-active) 116 | stroke-width 2px 117 | stroke-dasharray 3 2 118 | fill none 119 | 120 | .narrow-stroke 121 | stroke var(--color-active) 122 | stroke-width 2px 123 | fill none 124 | 125 | .fill 126 | fill var(--color-active) 127 | 128 | .text 129 | font-weight 700 130 | font-family var(--font-monospace) 131 | pointer-events none 132 | fill var(--color-active) 133 | -------------------------------------------------------------------------------- /src/style/config.styl: -------------------------------------------------------------------------------- 1 | $popover-arrow-size = 0.7em 2 | $border-radius = 3px 3 | $border-radius-large = 6px 4 | $color-preview-size = 60px 5 | 6 | // TODO: Fix 7 | input-border-style() 8 | height var(--layout-input-height) 9 | border 1px solid var(--color-border) 10 | border-radius $border-radius 11 | background var(--color-field) 12 | 13 | input-placement-modifier-root() 14 | z-index 1 15 | 16 | &:hover 17 | z-index 2 18 | 19 | &:active, &:focus, &.editing, &.selected, &.updating 20 | z-index 3 21 | 22 | &.left 23 | margin-right -1px 24 | 25 | &.center 26 | margin-right -1px 27 | 28 | &.top 29 | margin-bottom -1px 30 | 31 | &.middle 32 | margin-bottom -1px 33 | 34 | input-placement-modifier-border(prefix = '&', suffix = '') 35 | {prefix}.left{suffix} 36 | border-top-right-radius 0 37 | border-bottom-right-radius 0 38 | 39 | {prefix}.center{suffix} 40 | border-radius 0 41 | 42 | {prefix}.right{suffix} 43 | border-top-left-radius 0 44 | border-bottom-left-radius 0 45 | 46 | {prefix}.top{suffix} 47 | border-bottom-right-radius 0 48 | border-bottom-left-radius 0 49 | 50 | {prefix}.middle{suffix} 51 | border-radius 0 52 | 53 | {prefix}.bottom{suffix} 54 | border-top-left-radius 0 55 | border-top-right-radius 0 56 | 57 | input-border-hover-style() 58 | border-color var(--color-active) 59 | 60 | input-border-focus-style() 61 | border-color var(--color-active) 62 | box-shadow 0 0 0 1px @border-color 63 | 64 | input-field-style() 65 | padding 0 0.3em 66 | color var(--color-text) 67 | line-height calc(var(--layout-input-height) - 2px) 68 | 69 | enable-menu-color() 70 | --color-bg var(--color-menu-bg) 71 | --color-text var(--color-menu-text) 72 | --color-border var(--color-menu-border) 73 | --color-field var(--color-menu-field) 74 | 75 | color var(--color-text) 76 | background var(--color-bg) 77 | -------------------------------------------------------------------------------- /src/util/MouseDragEvent.ts: -------------------------------------------------------------------------------- 1 | import {vec2} from 'gl-matrix' 2 | 3 | export default interface MouseDragEvent { 4 | current: vec2 5 | delta: vec2 6 | offset: vec2 7 | abort: () => void 8 | originalEvent: any 9 | } 10 | -------------------------------------------------------------------------------- /src/util/RoteryDrag.ts: -------------------------------------------------------------------------------- 1 | import {vec2} from 'gl-matrix' 2 | import {lerp} from '../math' 3 | 4 | // Returns +1, -1, 0 5 | function isTraversedAxisMinusX( 6 | p1: ArrayLike, 7 | p2: ArrayLike 8 | ): number { 9 | // Suppose a line segment from p1 to p2. 10 | // It'd be okay to check if it intersects to the axis -X 11 | // Precisely, The axis's formula is y = lim(a -> 0) a, x < 0 12 | 13 | const isTraversedAxisX = p1[1] * p2[1] < 0 || (p1[1] === 0 && p2[1] > 0) 14 | 15 | if (isTraversedAxisX) { 16 | // calculate X coord of intersection to the axis 17 | const yabs1 = Math.abs(p1[1]) 18 | const yabs2 = Math.abs(p2[1]) 19 | const t = yabs1 / (yabs1 + yabs2) 20 | const intersectionX = lerp(p1[0], p2[0], t) 21 | 22 | if (intersectionX < 0) { 23 | // Traversed 24 | return Math.sign(p2[1]) 25 | } 26 | } 27 | 28 | return 0 29 | } 30 | 31 | export default class RoteryDrag { 32 | public minDistance: number = 0 33 | public initialAngle!: number 34 | 35 | private baseRotation!: number 36 | private lastAngle!: number 37 | private prev: vec2 = vec2.create() 38 | private current: vec2 = vec2.create() 39 | private center: vec2 = vec2.create() 40 | 41 | public start( 42 | initialAngle: number, 43 | center: number[], 44 | position: number[] 45 | ): void { 46 | this.initialAngle = initialAngle 47 | this.lastAngle = initialAngle 48 | vec2.copy(this.center, center) 49 | vec2.sub(this.prev, position, center) 50 | this.baseRotation = Math.floor(initialAngle / 360) 51 | } 52 | 53 | public getAngle(position: number[]): number { 54 | vec2.sub(this.current, position, this.center) 55 | 56 | if (vec2.length(this.current) < this.minDistance) { 57 | return this.lastAngle 58 | } 59 | 60 | const traversed = isTraversedAxisMinusX(this.prev, this.current) 61 | this.baseRotation -= traversed 62 | 63 | const angle = (Math.atan2(this.current[1], this.current[0]) / Math.PI) * 180 64 | this.lastAngle = this.baseRotation * 360 + angle 65 | 66 | vec2.copy(this.prev, this.current) 67 | 68 | return this.lastAngle 69 | } 70 | 71 | public get radius() { 72 | return vec2.length(this.current) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/util/constrain-value.ts: -------------------------------------------------------------------------------- 1 | import {quantize} from '../math' 2 | 3 | type ValueType = number | number[] 4 | 5 | interface ConstraintsInfo { 6 | min: ValueType 7 | max: ValueType 8 | step: number 9 | toData: (value: ValueType) => ValueType 10 | } 11 | 12 | export default function constrainValue( 13 | value: any, 14 | {min, max, step, toData}: ConstraintsInfo 15 | ): any { 16 | const isValueArray = Array.isArray(value) 17 | 18 | if (typeof min === 'boolean') { 19 | if (isValueArray) { 20 | const isMinArray = Array.isArray(min) 21 | for (let i = 0; i < value.length; i++) { 22 | value[i] = Math.max(value[i], isMinArray ? min[i] : min) 23 | } 24 | } else { 25 | value = Math.max(value, min) 26 | } 27 | } 28 | 29 | if (typeof max === 'boolean') { 30 | if (isValueArray) { 31 | const iMaxArray = Array.isArray(max) 32 | for (let i = 0; i < value.length; i++) { 33 | value[i] = Math.min(value[i], iMaxArray ? max[i] : max) 34 | } 35 | } else { 36 | value = Math.min(value, max) 37 | } 38 | } 39 | 40 | if (typeof step === 'number') { 41 | if (isValueArray) { 42 | for (let i = 0; i < value.length; i++) { 43 | value[i] = quantize(value[i], step) 44 | } 45 | } else { 46 | value = quantize(value, step) 47 | } 48 | } 49 | 50 | if (toData) { 51 | value = toData(value) 52 | } 53 | 54 | return value 55 | } 56 | -------------------------------------------------------------------------------- /src/util/deepcopy.ts: -------------------------------------------------------------------------------- 1 | function isPlainObject(obj: any) { 2 | return obj !== null && typeof obj === 'object' 3 | } 4 | 5 | export default function deepcopy(src: any) { 6 | let dest: any 7 | if (Array.isArray(src)) { 8 | dest = src.slice(0) || [] 9 | dest.forEach((n: any) => { 10 | if ((typeof n === 'object' && n !== {}) || Array.isArray(n)) { 11 | n = deepcopy(n) 12 | } 13 | }) 14 | } else if (typeof src.clone === 'function') { 15 | dest = src.clone() 16 | } else if (isPlainObject(src)) { 17 | dest = Object.assign({}, src) 18 | Object.keys(dest).forEach(key => { 19 | if (isPlainObject(dest[key])) { 20 | dest[key] = deepcopy(dest[key]) 21 | } 22 | }) 23 | } else { 24 | dest = src 25 | } 26 | return dest 27 | } 28 | -------------------------------------------------------------------------------- /src/util/deserialize.ts: -------------------------------------------------------------------------------- 1 | import Color from '../data/Color' 2 | 3 | const DataClass: {[s: string]: any} = { 4 | Color 5 | } 6 | 7 | export default function deserialize(text: string) { 8 | const data = JSON.parse(text) 9 | 10 | if (/^(object|array)$/.test(typeof data)) { 11 | deserializeObject(data) 12 | } 13 | return data 14 | 15 | function deserializeObject(obj: any) { 16 | const keys = Array.isArray(obj) 17 | ? Array(obj.length) 18 | .fill(0) 19 | .map((_, i) => i) 20 | : Object.keys(obj) 21 | 22 | for (const key of keys) { 23 | const value = obj[key] 24 | if ( 25 | Array.isArray(value) && 26 | typeof value[0] === 'string' && 27 | value[0] === ':' 28 | ) { 29 | // It should be an instance of class 30 | } else if (typeof value === 'object') { 31 | if (typeof value.$type === 'string') { 32 | obj[key] = new DataClass[value.$type](...value.value) 33 | } else { 34 | deserializeObject(value) 35 | } 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/util/disable-reactive.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baku89/ui-study/99799e504df0f74ee5838e81ee5258387df2a99e/src/util/disable-reactive.ts -------------------------------------------------------------------------------- /src/util/force-notify.ts: -------------------------------------------------------------------------------- 1 | export default function forceNotify(obj: any) { 2 | if (obj.__ob__) { 3 | obj.__ob__.dep.notify() 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/util/index.ts: -------------------------------------------------------------------------------- 1 | import mouse from 'mouse-event' 2 | 3 | import constrainValue from './constrain-value' 4 | import deserialize from './deserialize' 5 | import forceNotify from './force-notify' 6 | import RoteryDrag from './RoteryDrag' 7 | import MouseDragEvent from './MouseDragEvent' 8 | import splitToParentAndKey from './split-to-parent-and-key' 9 | 10 | function getDOMCenter(el: HTMLElement): number[] { 11 | const {top, right, bottom, left} = el.getBoundingClientRect() 12 | return [(left + right) / 2, (top + bottom) / 2] 13 | } 14 | 15 | function setButtonUnfocusableForMouse(button: HTMLElement) { 16 | button.addEventListener('mousedown', (e: MouseEvent) => { 17 | const clicked = mouse.buttons(e) 18 | if (clicked === 1) { 19 | window.addEventListener( 20 | 'mouseup', 21 | () => { 22 | button.blur() 23 | }, 24 | {once: true} 25 | ) 26 | } 27 | }) 28 | } 29 | export { 30 | constrainValue, 31 | deserialize, 32 | forceNotify, 33 | getDOMCenter, 34 | setButtonUnfocusableForMouse, 35 | RoteryDrag, 36 | MouseDragEvent, 37 | splitToParentAndKey 38 | } 39 | -------------------------------------------------------------------------------- /src/util/split-to-parent-and-key.ts: -------------------------------------------------------------------------------- 1 | export default function splitToParentAndKey(path: string) { 2 | const lastSlashIndex = path.lastIndexOf('/') 3 | 4 | const parent = path.substr(0, lastSlashIndex) 5 | let key: string | number = path.substr(lastSlashIndex + 1) 6 | 7 | if (/^[0-9]+$/.test(key)) { 8 | key = parseInt(key, 10) 9 | } 10 | 11 | return [parent, key] 12 | } 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "esnext", 5 | "strict": true, 6 | "strictFunctionTypes": false, 7 | "jsx": "preserve", 8 | "importHelpers": true, 9 | "moduleResolution": "node", 10 | "experimentalDecorators": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "sourceMap": true, 14 | "declaration": true, 15 | "declarationDir": "./typings", 16 | "baseUrl": ".", 17 | "lib": ["esnext", "dom", "dom.iterable", "scripthost"] 18 | }, 19 | "files": ["node_modules/@types/webgl2/index.d.ts"], 20 | "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"], 21 | "exclude": ["node_modules"] 22 | } 23 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "warning", 3 | "extends": ["tslint:recommended", "tslint-config-prettier"], 4 | "linterOptions": { 5 | "exclude": ["node_modules/**"] 6 | }, 7 | "rules": { 8 | "interface-name": false, 9 | "ordered-imports": false, 10 | "object-literal-sort-keys": false, 11 | "no-consecutive-blank-lines": false, 12 | "no-console": false, 13 | "variable-name": [true, "allow-leading-underscore", "allow-pascal-case"], 14 | "one-variable-per-declaration": false, 15 | "max-classes-per-file": false 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /typings/components/ConfigProvider.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class ConfigProvider extends Vue { 3 | private keySlower; 4 | private keyFaster; 5 | private keySymmetry; 6 | private keyQuantize; 7 | private keyScale; 8 | private quantizeAngles; 9 | private render; 10 | } 11 | -------------------------------------------------------------------------------- /typings/components/InputAngle.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class InputAngle extends Vue { 3 | private value; 4 | private isDragging; 5 | private dragFrom; 6 | private dragTo; 7 | private roteryDrag; 8 | private readonly quantizeAngles; 9 | private readonly keyQuantize; 10 | private created; 11 | private mounted; 12 | private onDragstart; 13 | private onDrag; 14 | private onDragend; 15 | } 16 | -------------------------------------------------------------------------------- /typings/components/InputButton.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class InputButton extends Vue { 3 | private label; 4 | private icon; 5 | private iconPosition; 6 | private mounted; 7 | private readonly onlyIcon; 8 | } 9 | -------------------------------------------------------------------------------- /typings/components/InputCheckbox.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class InputCheckbox extends Vue { 3 | private value; 4 | private label; 5 | private id; 6 | } 7 | -------------------------------------------------------------------------------- /typings/components/InputCodeEditor/InputCodeEditor.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class InputCodeEditor extends Vue { 3 | private value; 4 | private lang; 5 | private editor; 6 | private mounted; 7 | private beforeDestroy; 8 | private onValueChanged; 9 | private onLangChanged; 10 | } 11 | -------------------------------------------------------------------------------- /typings/components/InputCodeEditor/index.d.ts: -------------------------------------------------------------------------------- 1 | import InputCodeEditor from './InputCodeEditor.vue'; 2 | export default InputCodeEditor; 3 | -------------------------------------------------------------------------------- /typings/components/InputColor/InputColor.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class InputColor extends Vue { 3 | private value; 4 | private showLabel; 5 | private readonly hasAlpha; 6 | private readonly isHex; 7 | private validateColorHex; 8 | private onUpdateElement; 9 | } 10 | -------------------------------------------------------------------------------- /typings/components/InputColor/InputColorElement.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | import { DataColorMode, DataColorModeInfo } from '../../data'; 3 | export default class InputColorElement extends Vue { 4 | private color; 5 | private varying; 6 | private isEditing; 7 | private isDragging; 8 | private slitMaxY; 9 | private slitMinY; 10 | private slitLeft; 11 | private previewY; 12 | private updatedRecently; 13 | private updatedTimer; 14 | private readonly keyFaster; 15 | readonly mode: DataColorMode; 16 | readonly element: number; 17 | readonly cssColor: string; 18 | readonly slitStyles: { 19 | left: string; 20 | top: string; 21 | height: string; 22 | }; 23 | readonly previewStyles: { 24 | background: string; 25 | left: string; 26 | top: string; 27 | }; 28 | readonly info: DataColorModeInfo; 29 | private onChange; 30 | private onKeydown; 31 | private onClick; 32 | private onDragstart; 33 | private onDrag; 34 | private onDragend; 35 | private onColorChanged; 36 | } 37 | -------------------------------------------------------------------------------- /typings/components/InputColor/index.d.ts: -------------------------------------------------------------------------------- 1 | import InputColor from './InputColor.vue'; 2 | export default InputColor; 3 | -------------------------------------------------------------------------------- /typings/components/InputColorButton.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | import { DataColorMode, DataColorElements } from '../data'; 3 | export default class InputColorButton extends Vue { 4 | private value; 5 | private isPopoverOpen; 6 | readonly mode: DataColorMode; 7 | readonly elements: DataColorElements; 8 | readonly cssColor: string; 9 | readonly previewStyles: object; 10 | readonly hsl: number[]; 11 | private onChangeMode; 12 | private onInput; 13 | } 14 | -------------------------------------------------------------------------------- /typings/components/InputColorPicker.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class InputColorPicker extends Vue { 3 | private value; 4 | private isDraggingSV; 5 | private isDraggingHue; 6 | private readonly mode; 7 | private readonly elements; 8 | private readonly hsv; 9 | private readonly cssColor; 10 | private readonly SVPreviewStyles; 11 | private readonly HuePreviewStyles; 12 | private readonly gradientPaletteColor; 13 | private onDragSV; 14 | private onDragHue; 15 | private emitNewValue; 16 | } 17 | -------------------------------------------------------------------------------- /typings/components/InputDropdown.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class InputDropdown extends Vue { 3 | private value; 4 | private values; 5 | private labels; 6 | private theme; 7 | private onChange; 8 | } 9 | -------------------------------------------------------------------------------- /typings/components/InputIconAction/InputIconAction.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class InputIconAction extends Vue { 3 | private items; 4 | private src; 5 | private isPopoverOpen; 6 | private onClick; 7 | } 8 | -------------------------------------------------------------------------------- /typings/components/InputIconAction/index.d.ts: -------------------------------------------------------------------------------- 1 | import InputIconAction from './InputIconAction.vue'; 2 | export default InputIconAction; 3 | -------------------------------------------------------------------------------- /typings/components/InputIconButton.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class InputIconButton extends Vue { 3 | private src; 4 | private mounted; 5 | } 6 | -------------------------------------------------------------------------------- /typings/components/InputIconToggle.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class InputIconToggle extends Vue { 3 | private value; 4 | private srcOn; 5 | private srcOff; 6 | private mounted; 7 | } 8 | -------------------------------------------------------------------------------- /typings/components/InputMatrix.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class InputMatrix extends Vue { 3 | private value; 4 | private columns; 5 | private rows; 6 | private direction; 7 | private precision; 8 | private min; 9 | private max; 10 | private labels; 11 | private unit; 12 | private readonly elmAttrs; 13 | private onInput; 14 | } 15 | -------------------------------------------------------------------------------- /typings/components/InputMode.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class InputMode extends Vue { 3 | private value; 4 | private values; 5 | private labels; 6 | private id; 7 | private onClick; 8 | } 9 | -------------------------------------------------------------------------------- /typings/components/InputNumber.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class InputNumber extends Vue { 3 | isSelected: boolean; 4 | private value; 5 | private precision; 6 | private label; 7 | private unit; 8 | private min; 9 | private max; 10 | private step; 11 | private isEditing; 12 | private isDragging; 13 | private dragFrom; 14 | private dragTo; 15 | private dragMinX; 16 | private dragMaxX; 17 | private shouldOmitZero; 18 | private updatedRecently; 19 | private updatedTimer; 20 | private readonly SelectionManager; 21 | private readonly dragSpeed; 22 | private readonly keyFaster; 23 | private readonly keySlower; 24 | private readonly displayValue; 25 | private readonly hasMin; 26 | private readonly hasMax; 27 | private readonly hasStep; 28 | private onChange; 29 | private updateValue; 30 | private onClick; 31 | private onFocus; 32 | private onKeydown; 33 | private onDragstart; 34 | private onDrag; 35 | private onDragend; 36 | private onValueChanged; 37 | } 38 | -------------------------------------------------------------------------------- /typings/components/InputPoint.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class InputPoint extends Vue { 3 | private value; 4 | private knobOffset; 5 | private isDragging; 6 | private dragFrom; 7 | private dragTo; 8 | private readonly keyFaster; 9 | private readonly keySlower; 10 | private ui; 11 | private onKeydown; 12 | private onKeyup; 13 | private onDragstart; 14 | private onDrag; 15 | private onDragend; 16 | } 17 | -------------------------------------------------------------------------------- /typings/components/InputRange.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class InputRange extends Vue { 3 | private value; 4 | private min; 5 | private max; 6 | private step; 7 | private readonly keySymmetry; 8 | private readonly keySlower; 9 | private hoverTarget; 10 | private dragMode; 11 | private dragStartValue; 12 | readonly lower: number; 13 | readonly upper: number; 14 | readonly barStyles: { 15 | left: string; 16 | right: string; 17 | }; 18 | readonly firstStyles: { 19 | left: string; 20 | }; 21 | readonly secondStyles: { 22 | left: string; 23 | }; 24 | private onMousemove; 25 | private onMouseleave; 26 | private onDragstart; 27 | private onDrag; 28 | private onDragend; 29 | } 30 | -------------------------------------------------------------------------------- /typings/components/InputSlider.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class InputSlider extends Vue { 3 | private value; 4 | private min; 5 | private max; 6 | private step; 7 | private readonly keySlower; 8 | private dragStartValue; 9 | private isDragging; 10 | private readonly percent; 11 | private readonly accumStyles; 12 | private readonly knobStyles; 13 | private readonly isExceeded; 14 | private onDragstart; 15 | private onDrag; 16 | private onDragend; 17 | } 18 | -------------------------------------------------------------------------------- /typings/components/InputString.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class InputString extends Vue { 3 | private value; 4 | private validator; 5 | readonly inputListeners: Record & { 6 | change: () => void; 7 | }; 8 | private onChange; 9 | } 10 | -------------------------------------------------------------------------------- /typings/components/InputTime/InputTime.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class InputTime extends Vue { 3 | private value; 4 | private min; 5 | private max; 6 | private readonly fps; 7 | private readonly dragSpeed; 8 | private readonly keyFaster; 9 | private readonly keySlower; 10 | private smpte; 11 | private frames; 12 | private dropFrameSeparator; 13 | private seconds; 14 | private minutes; 15 | private hours; 16 | private isEditing; 17 | private isDragging; 18 | private activePartIndex; 19 | private dragFrom; 20 | private dragTo; 21 | private dragMinX; 22 | private dragMaxX; 23 | private timecode; 24 | private readonly hasMin; 25 | private readonly hasMax; 26 | private created; 27 | private onValueChanged; 28 | private onChange; 29 | private onKeydown; 30 | private onMouseenterPart; 31 | private onMouseleavePart; 32 | private onDragstart; 33 | private onDrag; 34 | private onDragend; 35 | private onClick; 36 | private setSelectionByPartIndex; 37 | } 38 | -------------------------------------------------------------------------------- /typings/components/InputTime/index.d.ts: -------------------------------------------------------------------------------- 1 | import InputTime from './InputTime.vue'; 2 | export default InputTime; 3 | -------------------------------------------------------------------------------- /typings/components/InputVector.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class InputVector extends Vue { 3 | private value; 4 | private precision; 5 | private min; 6 | private max; 7 | private step; 8 | private labels; 9 | private unit; 10 | private onInput; 11 | } 12 | -------------------------------------------------------------------------------- /typings/components/ParamFieldAffine.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class ParamFieldAffine extends Vue { 3 | private value; 4 | private precision; 5 | private dragSpeed; 6 | private onInput; 7 | } 8 | -------------------------------------------------------------------------------- /typings/components/ParamFieldAngle.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class ParamFieldAngle extends Vue { 3 | private value; 4 | private precision; 5 | private label; 6 | private onInput; 7 | } 8 | -------------------------------------------------------------------------------- /typings/components/ParamFieldColor.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class ParamFieldColor extends Vue { 3 | private value; 4 | private onInput; 5 | } 6 | -------------------------------------------------------------------------------- /typings/components/ParamFieldNumber.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class ParamFieldNumber extends Vue { 3 | } 4 | -------------------------------------------------------------------------------- /typings/components/ParamFieldPoint.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class ParamFieldPoint extends Vue { 3 | private value; 4 | private precision; 5 | private label; 6 | private unit; 7 | private keepProportion; 8 | private onInput; 9 | } 10 | -------------------------------------------------------------------------------- /typings/components/ParamFieldRange.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class ParamFieldRange extends Vue { 3 | private value; 4 | private min; 5 | private max; 6 | private precision; 7 | private labels; 8 | private unit; 9 | private onInputVector; 10 | private onInput; 11 | } 12 | -------------------------------------------------------------------------------- /typings/components/ParamFieldScale.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class ParamFieldScale extends Vue { 3 | private value; 4 | private min; 5 | private max; 6 | private precision; 7 | private labels; 8 | private keepProportion; 9 | private internalKeepProportion; 10 | private created; 11 | private readonly _keepProportion; 12 | private onInput; 13 | private onChangeKeepProportion; 14 | } 15 | -------------------------------------------------------------------------------- /typings/components/ParamFieldSeed.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class ParamFieldSeed extends Vue { 3 | private value; 4 | private min; 5 | private max; 6 | private step; 7 | private preision; 8 | private onInput; 9 | private generateRandomSeed; 10 | } 11 | -------------------------------------------------------------------------------- /typings/components/ParamFieldSlider.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class ParamFieldSlider extends Vue { 3 | private value; 4 | private min; 5 | private max; 6 | private step; 7 | private precision; 8 | private label; 9 | private unit; 10 | private onInput; 11 | } 12 | -------------------------------------------------------------------------------- /typings/components/Parameter.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class Parameter extends Vue { 3 | private label; 4 | } 5 | -------------------------------------------------------------------------------- /typings/components/SelectionManager.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class SelectionManager extends Vue { 3 | SelectionManager: this; 4 | private items; 5 | private dragStartValues; 6 | private dragMode; 7 | add(item: any): void; 8 | private mounted; 9 | private beforeDestroy; 10 | private readonly showControl; 11 | private deselectAll; 12 | private onDragstart; 13 | private onDrag; 14 | private onDragend; 15 | private deselectOnClickOutside; 16 | } 17 | -------------------------------------------------------------------------------- /typings/components/Timeline/Timeline.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class Timeline extends Vue { 3 | private time; 4 | private min; 5 | private max; 6 | private autoScroll; 7 | private readonly keyScale; 8 | private displayRange; 9 | private readonly keySlower; 10 | private dragStartTime; 11 | private created; 12 | private mounted; 13 | private readonly knobOverflow; 14 | private readonly knobStyles; 15 | private onDragstart; 16 | private onDrag; 17 | private onUpdateDisplayRange; 18 | private onTimeChanged; 19 | private scrollToTime; 20 | } 21 | -------------------------------------------------------------------------------- /typings/components/Timeline/TimelineSeekbarScale.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class TimelineSeekbarScale extends Vue { 3 | private displayRange; 4 | private readonly fps; 5 | private scales; 6 | private mounted; 7 | private updateScale; 8 | } 9 | -------------------------------------------------------------------------------- /typings/components/Timeline/index.d.ts: -------------------------------------------------------------------------------- 1 | import Timeline from './Timeline.vue'; 2 | export default Timeline; 3 | -------------------------------------------------------------------------------- /typings/components/TimelineColor.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class TimelineColor extends Vue { 3 | private value; 4 | private displayRange; 5 | private ctx; 6 | renderColors(): void; 7 | private mounted; 8 | private beforeDestroy; 9 | } 10 | -------------------------------------------------------------------------------- /typings/components/common/ButtonWrapper.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class ButtonWrapper extends Vue { 3 | private mounted; 4 | private initEventHandlers; 5 | private render; 6 | } 7 | -------------------------------------------------------------------------------- /typings/components/common/Drag.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class Drag extends Vue { 3 | private measure; 4 | private coordinate; 5 | private detectDirection; 6 | private minDragDistance; 7 | private clamp; 8 | private box; 9 | private dragStarted; 10 | private origin; 11 | private current; 12 | private prev; 13 | private delta; 14 | private absOrigin; 15 | private absCurrent; 16 | private absPrev; 17 | private created; 18 | private mounted; 19 | private beforeDestroy; 20 | private readonly boxElement; 21 | private onMousedown; 22 | private onKeyToggle; 23 | private onMousemove; 24 | private onMouseup; 25 | private quitDrag; 26 | private setAbsCoordByMouseEvent; 27 | private toSpecifiedCoord; 28 | private render; 29 | } 30 | -------------------------------------------------------------------------------- /typings/components/common/GradientPalette/GradientPalette.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class GradientPalette extends Vue { 3 | private color; 4 | private varyings; 5 | private ctx; 6 | private canvas; 7 | private mounted; 8 | private onColorChanged; 9 | private onVaryingsChanged; 10 | private renderPad; 11 | } 12 | -------------------------------------------------------------------------------- /typings/components/common/GradientPalette/index.d.ts: -------------------------------------------------------------------------------- 1 | import GradientPalette from './GradientPalette.vue'; 2 | export default GradientPalette; 3 | -------------------------------------------------------------------------------- /typings/components/common/Icon.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class Icon extends Vue { 3 | private src; 4 | private tag; 5 | private size; 6 | private render; 7 | } 8 | -------------------------------------------------------------------------------- /typings/components/common/Menu/Menu.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class Menu extends Vue { 3 | private items; 4 | private filterText; 5 | private readonly isFiltering; 6 | private readonly numValues; 7 | private readonly flattenedItems; 8 | private readonly filteredItems; 9 | private mounted; 10 | private beforeDestroy; 11 | private onKeydown; 12 | private onInputFilterText; 13 | private onKeydownFilterText; 14 | } 15 | -------------------------------------------------------------------------------- /typings/components/common/Menu/MenuItem.d.ts: -------------------------------------------------------------------------------- 1 | export default interface MenuItem { 2 | value: string | number | symbol; 3 | label: string; 4 | shortLabel?: string; 5 | icon?: string; 6 | type?: 'submenu' | 'compact'; 7 | submenu?: MenuItem[]; 8 | } 9 | -------------------------------------------------------------------------------- /typings/components/common/Menu/Submenu.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class Submenu extends Vue { 3 | private items; 4 | private type; 5 | private hasParent; 6 | private selectedIndex; 7 | private isSubSelected; 8 | setSelectedIndex(index: number | null): void; 9 | private onItemsChanged; 10 | private liClasses; 11 | private onHoverItem; 12 | private mounted; 13 | private beforeDestroy; 14 | private onKeydown; 15 | private selectNeighbour; 16 | } 17 | -------------------------------------------------------------------------------- /typings/components/common/Menu/index.d.ts: -------------------------------------------------------------------------------- 1 | import Menu from './Menu.vue'; 2 | export default Menu; 3 | -------------------------------------------------------------------------------- /typings/components/common/Popover.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class Popover extends Vue { 3 | private active; 4 | private placement; 5 | private popperInstance; 6 | private originalParentEl; 7 | setReference(el: Element): void; 8 | private mounted; 9 | private onActiveChanged; 10 | private setOriginalParent; 11 | private killPopper; 12 | private bindPopper; 13 | private createPopper; 14 | private resetPopper; 15 | } 16 | -------------------------------------------------------------------------------- /typings/components/common/Portal.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class Portal extends Vue { 3 | private originalParentEl; 4 | private attachToParent; 5 | private target; 6 | private mounted; 7 | private beforeDestroy; 8 | private render; 9 | private onTargetChanged; 10 | private killGhostElement; 11 | private initDestroy; 12 | private destroyElement; 13 | private changeParentEl; 14 | } 15 | -------------------------------------------------------------------------------- /typings/components/common/SvgArcArrow.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | export default class SvgArcArrow extends Vue { 3 | private center; 4 | private radius; 5 | private start; 6 | private end; 7 | readonly diff: number; 8 | readonly startRadians: number; 9 | readonly endRadians: number; 10 | readonly x2: number; 11 | readonly y2: number; 12 | readonly d: string; 13 | readonly willShowTip: boolean; 14 | readonly tipTransform: string; 15 | } 16 | -------------------------------------------------------------------------------- /typings/components/common/SvgArrow.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'vue-property-decorator'; 2 | import { vec2 } from 'gl-matrix'; 3 | export default class SvgArrow extends Vue { 4 | private from; 5 | private to; 6 | private _sub; 7 | private _lineTo; 8 | private _normalized; 9 | private created; 10 | readonly lineTo: vec2; 11 | readonly sub: vec2; 12 | readonly willShowTip: boolean; 13 | readonly tipTransform: string; 14 | } 15 | -------------------------------------------------------------------------------- /typings/components/index.d.ts: -------------------------------------------------------------------------------- 1 | import ConfigProvider from './ConfigProvider'; 2 | import InputAngle from './InputAngle.vue'; 3 | import InputButton from './InputButton.vue'; 4 | import InputCodeEditor from './InputCodeEditor'; 5 | import InputCheckbox from './InputCheckbox.vue'; 6 | import InputColor from './InputColor'; 7 | import InputColorButton from './InputColorButton.vue'; 8 | import InputDropdown from './InputDropdown.vue'; 9 | import InputIconAction from './InputIconAction'; 10 | import InputIconButton from './InputIconButton.vue'; 11 | import InputIconToggle from './InputIconToggle.vue'; 12 | import InputMatrix from './InputMatrix.vue'; 13 | import InputMode from './InputMode.vue'; 14 | import InputNumber from './InputNumber.vue'; 15 | import InputPoint from './InputPoint.vue'; 16 | import InputRange from './InputRange.vue'; 17 | import InputSlider from './InputSlider.vue'; 18 | import InputString from './InputString.vue'; 19 | import InputTime from './InputTime'; 20 | import InputVector from './InputVector.vue'; 21 | import Parameter from './Parameter.vue'; 22 | import ParamFieldAffine from './ParamFieldAffine.vue'; 23 | import ParamFieldAngle from './ParamFieldAngle.vue'; 24 | import ParamFieldColor from './ParamFieldColor.vue'; 25 | import ParamFieldNumber from './ParamFieldNumber.vue'; 26 | import ParamFieldPoint from './ParamFieldPoint.vue'; 27 | import ParamFieldRange from './ParamFieldRange.vue'; 28 | import ParamFieldScale from './ParamFieldScale.vue'; 29 | import ParamFieldSeed from './ParamFieldSeed.vue'; 30 | import ParamFieldSlider from './ParamFieldSlider.vue'; 31 | import SelectionManager from './SelectionManager.vue'; 32 | import Timeline from './Timeline'; 33 | import TimelineColor from './TimelineColor.vue'; 34 | declare const _default: { 35 | ConfigProvider: typeof ConfigProvider; 36 | InputAngle: typeof InputAngle; 37 | InputButton: typeof InputButton; 38 | InputCodeEditor: typeof InputCodeEditor; 39 | InputCheckbox: typeof InputCheckbox; 40 | InputColor: typeof InputColor; 41 | InputColorButton: typeof InputColorButton; 42 | InputDropdown: typeof InputDropdown; 43 | InputIconAction: typeof InputIconAction; 44 | InputIconButton: typeof InputIconButton; 45 | InputIconToggle: typeof InputIconToggle; 46 | InputMatrix: typeof InputMatrix; 47 | InputMode: typeof InputMode; 48 | InputNumber: typeof InputNumber; 49 | InputPoint: typeof InputPoint; 50 | InputRange: typeof InputRange; 51 | InputSlider: typeof InputSlider; 52 | InputString: typeof InputString; 53 | InputTime: typeof InputTime; 54 | InputVector: typeof InputVector; 55 | Parameter: typeof Parameter; 56 | ParamFieldAffine: typeof ParamFieldAffine; 57 | ParamFieldAngle: typeof ParamFieldAngle; 58 | ParamFieldColor: typeof ParamFieldColor; 59 | ParamFieldNumber: typeof ParamFieldNumber; 60 | ParamFieldPoint: typeof ParamFieldPoint; 61 | ParamFieldRange: typeof ParamFieldRange; 62 | ParamFieldSeed: typeof ParamFieldSeed; 63 | ParamFieldScale: typeof ParamFieldScale; 64 | ParamFieldSlider: typeof ParamFieldSlider; 65 | SelectionManager: typeof SelectionManager; 66 | Timeline: typeof Timeline; 67 | TimelineColor: typeof TimelineColor; 68 | }; 69 | export default _default; 70 | -------------------------------------------------------------------------------- /typings/data/index.d.ts: -------------------------------------------------------------------------------- 1 | declare type DataColorMode = 'hex' | 'hexa' | 'rgb' | 'rgba' | 'hsl' | 'hsla' | 'hsv' | 'hsva'; 2 | declare type DataColorElements = string | number[] | [string, number]; 3 | declare type DataColor = [DataColorMode, DataColorElements]; 4 | interface DataColorModeInfo { 5 | max: number[]; 6 | label: string[]; 7 | unit: string[]; 8 | } 9 | declare const DataColorInfo: Map; 10 | declare type DataTransformType1D = 'translateX' | 'translateY' | 'scaleX' | 'scaleY' | 'scaleUniform' | 'rotate' | 'skewX' | 'skewY'; 11 | declare type DataTransformType2D = 'translate' | 'scale' | 'skew'; 12 | declare type DataTransformTypeMatrix = 'matrix'; 13 | declare type DataTransformType = DataTransformType1D | DataTransformType2D | DataTransformTypeMatrix; 14 | declare type DataTransformValue = number | number[]; 15 | declare const DataTransformType1DList: string[]; 16 | declare const DataTransformType2DList: string[]; 17 | declare const DataTransformTypeMatrixList: string[]; 18 | declare const DataTransformTypeList: string[]; 19 | interface DataTransformStack { 20 | type: DataTransformType; 21 | value: DataTransformValue; 22 | active: boolean; 23 | } 24 | declare type DataTransform = DataTransformStack[]; 25 | export { DataColorMode, DataColorElements, DataColor, DataColorModeInfo, DataColorInfo, DataTransform, DataTransformType, DataTransformValue, DataTransformType1DList, DataTransformType2DList, DataTransformTypeMatrixList, DataTransformTypeList, DataTransformStack }; 26 | -------------------------------------------------------------------------------- /typings/index.d.ts: -------------------------------------------------------------------------------- 1 | import Drag from './components/common/Drag'; 2 | import * as util from './util'; 3 | import * as math from './math'; 4 | declare const _default: { 5 | components: { 6 | ConfigProvider: typeof import("./components/ConfigProvider").default; 7 | InputAngle: typeof import("./components/InputAngle.vue").default; 8 | InputButton: typeof import("./components/InputButton.vue").default; 9 | InputCodeEditor: typeof import("./components/InputCodeEditor").default; 10 | InputCheckbox: typeof import("./components/InputCheckbox.vue").default; 11 | InputColor: typeof import("./components/InputColor").default; 12 | InputColorButton: typeof import("./components/InputColorButton.vue").default; 13 | InputDropdown: typeof import("./components/InputDropdown.vue").default; 14 | InputIconAction: typeof import("./components/InputIconAction").default; 15 | InputIconButton: typeof import("./components/InputIconButton.vue").default; 16 | InputIconToggle: typeof import("./components/InputIconToggle.vue").default; 17 | InputMatrix: typeof import("./components/InputMatrix.vue").default; 18 | InputMode: typeof import("./components/InputMode.vue").default; 19 | InputNumber: typeof import("./components/InputNumber.vue").default; 20 | InputPoint: typeof import("./components/InputPoint.vue").default; 21 | InputRange: typeof import("./components/InputRange.vue").default; 22 | InputSlider: typeof import("./components/InputSlider.vue").default; 23 | InputString: typeof import("./components/InputString.vue").default; 24 | InputTime: typeof import("./components/InputTime").default; 25 | InputVector: typeof import("./components/InputVector.vue").default; 26 | Parameter: typeof import("./components/Parameter.vue").default; 27 | ParamFieldAffine: typeof import("./components/ParamFieldAffine.vue").default; 28 | ParamFieldAngle: typeof import("./components/ParamFieldAngle.vue").default; 29 | ParamFieldColor: typeof import("./components/ParamFieldColor.vue").default; 30 | ParamFieldNumber: typeof import("./components/ParamFieldNumber.vue").default; 31 | ParamFieldPoint: typeof import("./components/ParamFieldPoint.vue").default; 32 | ParamFieldRange: typeof import("./components/ParamFieldRange.vue").default; 33 | ParamFieldSeed: typeof import("./components/ParamFieldSeed.vue").default; 34 | ParamFieldScale: typeof import("./components/ParamFieldScale.vue").default; 35 | ParamFieldSlider: typeof import("./components/ParamFieldSlider.vue").default; 36 | SelectionManager: typeof import("./components/SelectionManager.vue").default; 37 | Timeline: typeof import("./components/Timeline").default; 38 | TimelineColor: typeof import("./components/TimelineColor.vue").default; 39 | }; 40 | common: { 41 | Drag: typeof Drag; 42 | }; 43 | util: typeof util; 44 | math: typeof math; 45 | }; 46 | export default _default; 47 | -------------------------------------------------------------------------------- /typings/math/index.d.ts: -------------------------------------------------------------------------------- 1 | declare function lerp(a: number, b: number, t: number): number; 2 | declare function clamp(value: number, min: number, max: number): number; 3 | declare function mod(a: number, b: number): number; 4 | declare function ratio(value: number, bottom: number, top: number, clamped?: boolean): number; 5 | declare function parseNumber(str: string): number; 6 | declare function toFixed(value: number, precision: number, omitZeros?: boolean): string; 7 | declare function toRadians(degrees: number): number; 8 | declare function toDegrees(radians: number): number; 9 | declare function cycleMod(value: number, inc: number, max: number): number; 10 | declare function quantize(value: number, step: number): number; 11 | declare function isInteger(value: number): boolean; 12 | export { lerp, clamp, parseNumber, toFixed, ratio, mod, toRadians, toDegrees, cycleMod, quantize, isInteger }; 13 | -------------------------------------------------------------------------------- /typings/util/RoteryDrag.d.ts: -------------------------------------------------------------------------------- 1 | export default class RoteryDrag { 2 | minDistance: number; 3 | initialAngle: number; 4 | private baseRotation; 5 | private lastAngle; 6 | private prev; 7 | private current; 8 | private center; 9 | start(initialAngle: number, center: number[], position: number[]): void; 10 | getAngle(position: number[]): number; 11 | readonly radius: number; 12 | } 13 | -------------------------------------------------------------------------------- /typings/util/Timecode.d.ts: -------------------------------------------------------------------------------- 1 | export default class Timecode { 2 | static formatSimple(frameCount: number, frameRate: number): string; 3 | frameRate: number; 4 | readonly dropFrame: boolean; 5 | frameCount: number; 6 | frames: number; 7 | readonly framesText: string; 8 | seconds: number; 9 | readonly secondsText: string; 10 | minutes: number; 11 | readonly minutesText: string; 12 | hours: number; 13 | readonly hoursText: string; 14 | smpte: string; 15 | private _frameRate; 16 | private _dropFrame; 17 | private _frameCount; 18 | private _hours; 19 | private _minutes; 20 | private _seconds; 21 | private _frames; 22 | private _hoursText; 23 | private _minutesText; 24 | private _secondsText; 25 | private _framesText; 26 | constructor(frameCount?: number, frameRate?: number); 27 | private validateFrameRate; 28 | private updateSMTPE; 29 | } 30 | -------------------------------------------------------------------------------- /typings/util/index.d.ts: -------------------------------------------------------------------------------- 1 | import RoteryDrag from './RoteryDrag'; 2 | import keypressed from './keypressed'; 3 | import { DataColor, DataColorMode, DataColorElements } from '../data'; 4 | declare function getDOMCenter(el: HTMLElement): number[]; 5 | declare function setButtonUnfocusableForMouse(button: HTMLElement): void; 6 | declare function toCSSColor(color: DataColor): string; 7 | declare function convertColorElements(from: DataColorMode, to: DataColorMode, elements: DataColorElements): DataColorElements; 8 | export { getDOMCenter, setButtonUnfocusableForMouse, toCSSColor, convertColorElements, RoteryDrag, keypressed }; 9 | -------------------------------------------------------------------------------- /typings/util/keypressed.d.ts: -------------------------------------------------------------------------------- 1 | declare function keypressed(key: string): boolean; 2 | export default keypressed; 3 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | // resolve: { alias: { mobx: __dirname + "/node_modules/mobx/lib/mobx.es6.js" }} 2 | 3 | module.exports = { 4 | css: { 5 | loaderOptions: { 6 | stylus: { 7 | use: [require('autoprefixer-stylus')(), require('nib')()] 8 | } 9 | } 10 | }, 11 | configureWebpack: config => { 12 | if (process.env.NODE_ENV === 'production') { 13 | config.module.rules.forEach(rule => { 14 | if (rule.use) { 15 | let idx = rule.use.findIndex(w => w.loader === 'thread-loader') 16 | if (idx !== -1) rule.use.splice(idx, 1) 17 | } 18 | }) 19 | } 20 | }, 21 | chainWebpack: config => { 22 | config.resolve.extensions.prepend('.vue') 23 | 24 | config.module 25 | .rule('raw') 26 | .test(/\.(vert|frag|glsl)$/) 27 | .use('raw-loader') 28 | .loader('raw-loader') 29 | 30 | if (process.env.NODE_ENV === 'production') { 31 | // disable cache (not sure if this is actually useful...) 32 | config.module.rule('ts').uses.delete('cache-loader') 33 | 34 | config.module 35 | .rule('ts') 36 | .use('ts-loader') 37 | .loader('ts-loader') 38 | .tap(opts => { 39 | opts.transpileOnly = false 40 | opts.happyPackMode = false 41 | return opts 42 | }) 43 | } 44 | } 45 | } 46 | --------------------------------------------------------------------------------