diff options
Diffstat (limited to 'electron/src/renderer/assets/js/simulator.js')
| -rw-r--r-- | electron/src/renderer/assets/js/simulator.js | 654 |
1 files changed, 0 insertions, 654 deletions
diff --git a/electron/src/renderer/assets/js/simulator.js b/electron/src/renderer/assets/js/simulator.js deleted file mode 100644 index 26ad442e..00000000 --- a/electron/src/renderer/assets/js/simulator.js +++ /dev/null @@ -1,654 +0,0 @@ -/* - * Authors: see git history - * - * Copyright (c) 2010 Authors - * Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. - * - */ -import { inkStitch } from '../../../lib/api.js' - -import { SVG } from '@svgdotjs/svg.js' -import '@svgdotjs/svg.panzoom.js' -import '@svgdotjs/svg.filter.js' -import svgpath from 'svgpath' -import Loading from 'vue-loading-overlay'; -import 'vue-loading-overlay/dist/vue-loading.css'; -import { reactive, toRefs } from 'vue' -import VueSlider from 'vue-slider-component' -import 'vue-slider-component/theme/antd.css' -import throttle from 'lodash/throttle' - -function SliderMark(command, icon) { - this.label = "" - this.command = command - this.icon = icon -} - -export default { - name: 'simulator', - components: { - Loading, - VueSlider - }, - setup() { - const data = reactive({ value: 0 }) - return toRefs(data) - }, - data: function () { - return { - loading: false, - controlsExpanded: true, - infoExpanded: false, - infoMaxHeight: 0, - speed: 16, - currentStitch: 1, - currentStitchDisplay: 1, - direction: 1, - numStitches: 1, - animating: false, - sliderProcess: dotPos => this.sliderColorSections, - showTrims: false, - showJumps: false, - showColorChanges: false, - showStops: false, - showNeedlePenetrationPoints: false, - renderJumps: true, - showRealisticPreview: false, - showCursor: true, - error: false, - error_message: "" - } - }, - watch: { - currentStitch: throttle(function () { - this.currentStitchDisplay = Math.floor(this.currentStitch) - }, 100, {leading: true, trailing: true}), - showNeedlePenetrationPoints: function () { - if (this.needlePenetrationPoints === null) { - return; - } - - this.needlePenetrationPoints.forEach(npp => { - if (this.showNeedlePenetrationPoints) { - npp.show() - } else { - npp.hide() - } - }) - }, - renderJumps() { - this.renderedStitch = 1 - this.renderFrame() - }, - showRealisticPreview() { - let animating = this.animating - this.stop() - - if (this.showRealisticPreview) { - if (this.realisticPreview === null) { - // This workflow should be improved and might be a bit unconventional. - // We don't want to make the user wait for it too long. - // It would be best, if the realistic preview could load before it is actually requested. - this.$nextTick(() => {this.loading=true}) - setImmediate(()=> {this.generateRealisticPaths()}) - setImmediate(()=> {this.loading = false}) - } - - setImmediate(()=> { - this.renderedStitch = 0 - this.renderFrame() - - this.simulation.hide() - this.realisticPreview.show() - }) - - } else { - this.renderedStitch = 0 - this.renderFrame() - - this.simulation.show() - this.realisticPreview.hide() - } - - if (animating) { - this.start() - } - }, - showCursor: function () { - if (this.showCursor) { - this.cursor.show() - } else { - this.cursor.hide() - } - } - }, - computed: { - speedDisplay() { - return this.speed * this.direction - }, - currentCommand() { - let stitch = this.stitches[Math.floor(this.currentStitch)] - - if (stitch === undefined || stitch === null) { - return "" - } - - let label = this.$gettext("STITCH") - switch (true) { - case stitch.jump: - label = this.$gettext("JUMP") - break - case stitch.trim: - label = this.$gettext("TRIM") - break - case stitch.stop: - label = this.$gettext("STOP") - break - case stitch.color_change: - label = this.$gettext("COLOR CHANGE") - break - } - - return label - }, - paused() { - return !this.animating - }, - forward() { - return this.direction > 0 - }, - reverse() { - return this.direction < 0 - }, - sliderMarks() { - var marks = {} - - if (this.showTrims) - Object.assign(marks, this.trimMarks); - - if (this.showJumps) - Object.assign(marks, this.jumpMarks); - - if (this.showColorChanges) - Object.assign(marks, this.colorChangeMarks); - - if (this.showStops) - Object.assign(marks, this.stopMarks); - - return marks - } - }, - methods: { - toggleInfo() { - this.infoExpanded = !this.infoExpanded; - this.infoMaxHeight = this.$refs.controlInfoButton.getBoundingClientRect().top; - }, - toggleControls() { - this.controlsExpanded = !this.controlsExpanded; - }, - animationSpeedUp() { - this.speed *= 2.0 - }, - animationSlowDown() { - this.speed = Math.max(this.speed / 2.0, 1) - }, - animationReverse() { - this.direction = -1 - this.start() - }, - animationForward() { - this.direction = 1 - this.start() - }, - toggleAnimation(e) { - if (this.animating) { - this.stop() - } else { - this.start() - } - - e.preventDefault(); - }, - animationForwardOneStitch() { - this.setCurrentStitch(this.currentStitch + 1) - }, - animationBackwardOneStitch() { - this.setCurrentStitch(this.currentStitch - 1) - }, - animationNextCommand() { - let nextCommandIndex = this.getNextCommandIndex() - if (nextCommandIndex === -1) { - this.setCurrentStitch(this.stitches.length) - } else { - this.setCurrentStitch(this.commandList[nextCommandIndex]) - } - }, - animationPreviousCommand() { - let nextCommandIndex = this.getNextCommandIndex() - let prevCommandIndex = 0 - if (nextCommandIndex === -1) { - prevCommandIndex = this.commandList.length - 2 - } else { - prevCommandIndex = nextCommandIndex - 2 - } - let previousCommand = this.commandList[prevCommandIndex] - if (previousCommand === undefined) { - previousCommand = 1 - } - this.setCurrentStitch(previousCommand) - }, - getNextCommandIndex() { - let currentStitch = this.currentStitchDisplay - let nextCommand = this.commandList.findIndex(function (command) { - return command > currentStitch - }) - return nextCommand - }, - onCurrentStitchEntered() { - let newCurrentStitch = parseInt(this.$refs.currentStitchInput.value) - - if (isNaN(newCurrentStitch)) { - this.$refs.currentStitchInput.value = Math.floor(this.currentStitch) - } else { - this.setCurrentStitch(parseInt(newCurrentStitch)) - } - }, - setCurrentStitch(newCurrentStitch) { - this.stop() - this.currentStitch = newCurrentStitch - this.clampCurrentStitch() - this.renderFrame() - }, - clampCurrentStitch() { - this.currentStitch = Math.max(Math.min(this.currentStitch, this.numStitches), 0) - }, - animate() { - let frameStart = performance.now() - let frameTime = null - - if (this.lastFrameStart !== null) { - frameTime = frameStart - this.lastFrameStart - } else { - frameTime = this.targetFramePeriod - } - - this.lastFrameStart = frameStart - - let numStitches = this.speed * Math.max(frameTime, this.targetFramePeriod) / 1000.0; - this.currentStitch = this.currentStitch + numStitches * this.direction - this.clampCurrentStitch() - - this.renderFrame() - - if (this.animating && this.shouldAnimate()) { - this.timer = setTimeout(this.animate, Math.max(0, this.targetFramePeriod - frameTime)) - } else { - this.timer = null; - this.stop() - } - }, - renderFrame() { - while (this.renderedStitch < this.currentStitch) { - this.renderedStitch += 1 - if (!this.renderJumps && this.stitches[this.renderedStitch].jump){ - if (this.showRealisticPreview) { - this.realisticPaths[this.renderedStitch].hide(); - } else { - this.stitchPaths[this.renderedStitch].hide(); - } - continue; - } - if (this.showRealisticPreview) { - this.realisticPaths[this.renderedStitch].show() - } else { - this.stitchPaths[this.renderedStitch].show(); - } - } - - while (this.renderedStitch > this.currentStitch) { - if (this.showRealisticPreview) { - this.realisticPaths[this.renderedStitch].hide() - } else { - this.stitchPaths[this.renderedStitch].hide(); - } - this.renderedStitch -= 1 - } - - this.moveCursor() - }, - shouldAnimate() { - if (this.direction == 1 && this.currentStitch < this.numStitches) { - return true; - } else if (this.direction == -1 && this.currentStitch > 0) { - return true; - } else { - return false; - } - }, - start() { - if (!this.animating && this.shouldAnimate()) { - this.animating = true - this.timer = setTimeout(this.animate, 0); - } - }, - stop() { - if (this.animating) { - if (this.timer) { - clearTimeout(this.timer) - this.timer = null - } - this.animating = false - this.lastFrameStart = null - } - }, - resizeCursor() { - // This makes the cursor stay the same size when zooming in or out. - // I'm not exactly sure how it works, but it does. - this.cursor.size(25 / this.svg.zoom()) - this.cursor.stroke({width: 2 / this.svg.zoom()}) - - // SVG.js seems to move the cursor when we resize it, so we need to put - // it back where it goes. - this.moveCursor() - - this.adjustScale() - }, - moveCursor() { - let stitch = this.stitches[Math.floor(this.currentStitch)] - if (stitch === null || stitch === undefined) { - this.cursor.hide() - } else if (this.showCursor) { - this.cursor.show() - this.cursor.center(stitch.x, stitch.y) - } - }, - adjustScale: throttle(function () { - let one_mm = 96 / 25.4 * this.svg.zoom(); - let scaleWidth = one_mm - let simulatorWidth = this.$refs.simulator.getBoundingClientRect().width - let maxWidth = Math.min(simulatorWidth / 2, 300) - - while (scaleWidth > maxWidth) { - scaleWidth /= 2.0 - } - - while (scaleWidth < 100) { - scaleWidth += one_mm - } - - let scaleMM = scaleWidth / one_mm - - this.scale.plot(`M0,0 v10 h${scaleWidth / 2} v-5 v5 h${scaleWidth / 2} v-10`) - - // round and strip trailing zeros, source: https://stackoverflow.com/a/53397618 - let mm = scaleMM.toFixed(8).replace(/([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/, '$1') - this.scaleLabel.text(`${mm} mm`) - }, 100, {leading: true, trailing: true} - ), - generateMarks() { - this.commandList = Array() - for (let i = 1; i < this.stitches.length; i++) { - if (this.stitches[i].trim) { - this.trimMarks[i] = new SliderMark("trim", "cut") - this.commandList.push(i) - } else if (this.stitches[i].stop) { - this.stopMarks[i] = new SliderMark("stop", "pause") - this.commandList.push(i) - } else if (this.stitches[i].jump) { - this.jumpMarks[i] = new SliderMark("jump", "frog") - this.commandList.push(i) - } else if (this.stitches[i].color_change) { - this.colorChangeMarks[i] = new SliderMark("color-change", "exchange-alt") - this.commandList.push(i) - } - } - }, - generateColorSections() { - var currentStitch = 0 - this.stitchPlan.color_blocks.forEach(color_block => { - this.sliderColorSections.push([ - (currentStitch + 1) / this.numStitches * 100, - (currentStitch + color_block.stitches.length) / this.numStitches * 100, - {backgroundColor: color_block.color.visible_on_white.hex} - ]) - currentStitch += color_block.stitches.length - }) - }, - generateMarker(color) { - return this.svg.marker(3, 3, add => { - let needlePenetrationPoint = add.circle(3).fill(color).hide() - this.needlePenetrationPoints.push(needlePenetrationPoint) - }) - }, - generateScale() { - let svg = SVG().addTo(this.$refs.simulator) - svg.node.classList.add("simulation-scale") - this.scale = svg.path("M0,0").stroke({color: "black", width: "1px"}).fill("none") - this.scaleLabel = svg.text("0 mm").move(0, 12) - this.scaleLabel.node.classList.add("simulation-scale-label") - }, - generateCursor() { - this.cursor = - this.svg.path("M0,0 v2.8 h1.2 v-2.8 h2.8 v-1.2 h-2.8 v-2.8 h-1.2 v2.8 h-2.8 v1.2 h2.8") - .stroke({ - width: 0.1, - color: '#FFFFFF', - }) - .fill('#000000') - this.cursor.node.classList.add("cursor") - }, - generateRealisticPaths() { - - // Create Realistic Filter - this.filter = this.svg.defs().filter() - - this.filter.attr({id: "realistic-stitch-filter", x: "-0.1", y: "-0.1", height: "1.2", width: "1.2", style: "color-interpolation-filters:sRGB"}) - this.filter.gaussianBlur({id: "gaussianBlur1", stdDeviation: "1.5", in: "SourceAlpha"}) - this.filter.componentTransfer(function (add) { - add.funcR({ type: "identity" }), - add.funcG({ type: "identity" }), - add.funcB({ type: "identity", slope: "4.53" }), - add.funcA({ type: "gamma", slope: "0.149", intercept: "0", amplitude: "3.13", offset: "-0.33" }) - }).attr({id: "componentTransfer1", in: "gaussianBlur1"}) - this.filter.composite({id: "composite1", in: "componentTransfer1", in2: "SourceAlpha", operator: "in"}) - this.filter.gaussianBlur({id: "gaussianBlur2", in: "composite1", stdDeviation: 0.09}) - this.filter.morphology({id: "morphology1", in: "gaussianBlur2", operator: "dilate", radius: 0.1}) - this.filter.specularLighting({id: "specularLighting1", in: "morphology1", specularConstant: 0.709, surfaceScale: 30}).pointLight({z: 10}) - this.filter.gaussianBlur({id: "gaussianBlur3", in: "specularLighting1", stdDeviation: 0.04}) - this.filter.composite({id: "composite2", in: "gaussianBlur3", in2: "SourceGraphic", operator: "arithmetic", k2: 1, k3: 1, k1: 0, k4: 0}) - this.filter.composite({in: "composite2", in2: "SourceAlpha", operator: "in"}) - - // Create realistic paths in it's own group and move it behind the cursor - this.realisticPreview = this.svg.group({id: 'realistic'}).backward() - - this.stitchPlan.color_blocks.forEach(color_block => { - let color = `${color_block.color.visible_on_white.hex}` - let realistic_path_attrs = {fill: color, stroke: "none", filter: this.filter} - - let stitching = false - let prevStitch = null - color_block.stitches.forEach(stitch => { - - let realisticPath = null - if (stitching && prevStitch) { - - // Position - let stitch_center = [] - stitch_center.x = (prevStitch.x + stitch.x) / 2.0 - stitch_center.y = (prevStitch.y + stitch.y) / 2.0 - - // Angle - var stitch_angle = Math.atan2(stitch.y - prevStitch.y, stitch.x - prevStitch.x) * (180 / Math.PI) - - // Length - let path_length = Math.hypot(stitch.x - prevStitch.x, stitch.y - prevStitch.y) - - var path = `M0,0 c 0.4,0,0.4,0.3,0.4,0.6 c 0,0.3,-0.1,0.6,-0.4,0.6 v 0.2,-0.2 h -${path_length} c -0.4,0,-0.4,-0.3,-0.4,-0.6 c 0,-0.3,0.1,-0.6,0.4,-0.6 v -0.2,0.2 z` - path = svgpath(path).rotate(stitch_angle).toString() - - realisticPath = this.realisticPreview.path(path).attr(realistic_path_attrs).center(stitch_center.x, stitch_center.y).hide() - - } else { - realisticPath = this.realisticPreview.rect(0, 1).attr(realistic_path_attrs).center(stitch.x, stitch.y).hide() - } - - this.realisticPaths.push(realisticPath) - - if (stitch.trim || stitch.color_change) { - stitching = false - } else if (!stitch.jump) { - stitching = true - } - prevStitch = stitch - }) - }) - }, - generatePage () { - this.$refs.simulator.style.backgroundColor = this.page_specs.deskcolor - - let page = this.svg.rect(this.page_specs.width, this.page_specs.height) - .move(-this.stitchPlan.bounding_box[0],-this.stitchPlan.bounding_box[1]) - .fill(this.page_specs.pagecolor) - .stroke({width: 0.1, color: this.page_specs.bordercolor}) - .back() - - if (this.page_specs.showpageshadow === "true") { - let shadow = this.svg.rect(this.page_specs.width, this.page_specs.height) - .move(-this.stitchPlan.bounding_box[0],-this.stitchPlan.bounding_box[1]) - .fill(this.page_specs.bordercolor) - .filterWith(add => { - let blur = add.offset(.5,.5).in(add.$source).gaussianBlur(.5) - }) - .back() - } - - this.page_specs["bbox"] = page.bbox() - }, - zoomDesign () { - let [minx, miny, maxx, maxy] = this.stitchPlan.bounding_box - let designWidth = maxx - minx - let designHeight = maxy - miny - this.svg.viewbox(0, 0, designWidth, designHeight); - this.resizeCursor() - }, - zoomPage () { - this.svg.viewbox(this.page_specs.bbox.x, this.page_specs.bbox.y - 50, this.page_specs.bbox.width + 100, this.page_specs.bbox.height + 100) - this.resizeCursor() - }, - close () { - window.close() - } - }, - created: function () { - // non-reactive properties - this.targetFPS = 30 - this.targetFramePeriod = 1000.0 / this.targetFPS - this.renderedStitch = 0 - this.lastFrameStart = null - this.stitchPaths = [null] // 1-indexed to match up with stitch number display - this.realisticPaths = [null] - this.stitches = [null] - this.svg = null - this.simulation = null - this.realisticPreview = null - this.timer = null - this.sliderColorSections = [] - this.trimMarks = {} - this.stopMarks = {} - this.colorChangeMarks = {} - this.jumpMarks = {} - this.needlePenetrationPoints = [] - this.cursor = null - this.page_specs = {} - }, - mounted: function () { - this.svg = SVG().addTo(this.$refs.simulator).size('100%', '100%').panZoom({zoomMin: 0.1}) - this.svg.node.classList.add('simulation') - this.simulation = this.svg.group({id: 'line'}) - - this.loading = true - - inkStitch.get('stitch_plan').then(response => { - this.stitchPlan = response.data - let [minx, miny, maxx, maxy] = this.stitchPlan.bounding_box - let width = maxx - minx - let height = maxy - miny - this.svg.viewbox(0, 0, width, height); - - this.stitchPlan.color_blocks.forEach(color_block => { - let color = `${color_block.color.visible_on_white.hex}` - let path_attrs = {fill: "none", stroke: color, "stroke-width": 0.3} - let marker = this.generateMarker(color) - - let stitching = false - let prevStitch = null - color_block.stitches.forEach(stitch => { - stitch.x -= minx - stitch.y -= miny - - let path = null - if (stitching && prevStitch) { - path = this.simulation.path(`M${prevStitch.x},${prevStitch.y} ${stitch.x},${stitch.y}`).attr(path_attrs).hide() - } else { - path = this.simulation.path(`M${stitch.x},${stitch.y} ${stitch.x},${stitch.y}`).attr(path_attrs).hide() - } - path.marker('end', marker) - this.stitchPaths.push(path) - this.stitches.push(stitch) - - if (stitch.trim || stitch.color_change) { - stitching = false - } else if (!stitch.jump) { - stitching = true - } - - prevStitch = stitch - }) - }) - - this.numStitches = this.stitches.length - 1 - this.generateMarks() - this.generateColorSections() - this.generateScale() - this.generateCursor() - this.resizeCursor() - - this.loading = false - - // v-on:keydown doesn't seem to work, maybe an Electron issue? - this.$mousetrap.bind("up", this.animationSpeedUp) - this.$mousetrap.bind("down", this.animationSlowDown) - this.$mousetrap.bind("left", this.animationReverse) - this.$mousetrap.bind("right", this.animationForward) - this.$mousetrap.bind("pagedown", this.animationPreviousCommand) - this.$mousetrap.bind("pageup", this.animationNextCommand) - this.$mousetrap.bind("space", this.toggleAnimation) - this.$mousetrap.bind("+", this.animationForwardOneStitch) - this.$mousetrap.bind("-", this.animationBackwardOneStitch) - this.$mousetrap.bind("]", this.zoomDesign) - this.$mousetrap.bind("[", this.zoomPage) - - this.svg.on('zoom', this.resizeCursor) - - inkStitch.get('page_specs').then(response => { - this.page_specs = response.data - this.generatePage() - }) - - this.start() - }).catch(error => { - this.loading = false - if (error.response) { - // Stitch plan generation had an error. Show it to the user. - this.error_message = error.response.data.error_message - } else if (error.request) { - // We sent the request and didn't get a response. - this.error_message = "Stitch plan generation failed." - } else { - // Something weird happened in axios. - this.error_message = error.message - } - this.error = true - }) - } -} |
