-
-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(grabCanvas): Adds grab-canvas as a plugin (#612)
* feat(grabcanvas): adds grabCanvas plugin also adds the plugin panel and adds some plugin lifecycle functions #366 * feat(pluginprops): adds plugin props splits the Control component out into ModuleControl and PluginControl to allow code re-use. Adds store methods for plugin props. re #366 * fix: removes unnecessary gl-stack for plugins * feat: adds infoview directive for plugins panel * feat: adds searchterms directive * fix(grab-canvas): Clear mappingContext on every frame * refactor(grab-canvas): Set default size to 7x7 * refactor(grab-canvas): Proper reconnect and restructure of code Co-authored-by: Sam Wray <[email protected]>
1 parent
f9f2b90
commit d7ca887
Showing
12 changed files
with
626 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
const mappingCanvas = new OffscreenCanvas(1, 1); | ||
mappingCanvas.title = "mappingCanvas"; | ||
|
||
let timeout = 0; | ||
let connection = undefined; | ||
let outputContext = null; | ||
let reconnect = false; | ||
|
||
const mappingContext = mappingCanvas.getContext("2d", { | ||
// Boolean that indicates if the canvas contains an alpha channel. | ||
// If set to false, the browser now knows that the backdrop is always opaque, | ||
// which can speed up drawing of transparent content and images. | ||
// (lights don't have an alpha channel, so let's drop it) | ||
alpha: false, | ||
desynchronized: true, | ||
imageSmoothingEnabled: false | ||
}); | ||
|
||
export default { | ||
name: "Grab Canvas", | ||
props: { | ||
mappingWidth: { | ||
type: "int", | ||
default: 7, | ||
min: 1, | ||
max: 1024, | ||
step: 1, | ||
abs: true | ||
}, | ||
|
||
mappingHeight: { | ||
type: "int", | ||
default: 7, | ||
min: 1, | ||
max: 1024, | ||
step: 1, | ||
abs: true | ||
}, | ||
|
||
url: { | ||
type: "text", | ||
default: "ws://localhost:3006/modV" | ||
}, | ||
|
||
reconnectAfter: { | ||
type: "int", | ||
default: 4000, | ||
min: 1000, | ||
max: 60000, | ||
step: 1, | ||
abs: true | ||
}, | ||
|
||
shouldReconnect: { | ||
type: "bool", | ||
default: true | ||
} | ||
}, | ||
|
||
async init({ store, props }) { | ||
if (!outputContext) { | ||
outputContext = await store.dispatch("outputs/getAuxillaryOutput", { | ||
name: "Fixture Canvas", | ||
group: "Plugins", | ||
canvas: mappingCanvas, | ||
context: mappingContext, | ||
reactToResize: false | ||
}); | ||
} | ||
|
||
mappingCanvas.width = props.mappingWidth; | ||
mappingCanvas.height = props.mappingHeight; | ||
|
||
reconnect = props.shouldReconnect; | ||
|
||
this.setupSocket(props); | ||
}, | ||
|
||
shutdown() { | ||
this.stopReconnect(); | ||
this.closeConnection(); | ||
}, | ||
|
||
/** | ||
* Create a WebSocket for example to luminave | ||
*/ | ||
setupSocket(props) { | ||
const { url, reconnectAfter } = props; | ||
|
||
// Close an old connection | ||
this.closeConnection(); | ||
|
||
// Create a new connection | ||
connection = new WebSocket(url); | ||
|
||
// Listen for errors (e.g. could not connect) | ||
connection.addEventListener("error", event => { | ||
console.error("grab-canvas: WebSocket: Error:", event); | ||
|
||
// Reconnect is allowed | ||
if (reconnect) { | ||
// Reconnect after a specific amount of time | ||
timeout = setTimeout(() => { | ||
this.setupSocket(props); | ||
}, reconnectAfter); | ||
} | ||
}); | ||
|
||
// Connection is opened | ||
connection.addEventListener("open", () => { | ||
console.info("grab-canvas: WebSocket: Opened"); | ||
}); | ||
|
||
connection.addEventListener("close", () => { | ||
console.info("grab-canvas: WebSocket: Closed"); | ||
}); | ||
}, | ||
|
||
/** | ||
* Close the WebSocket connection and stop reconnecting | ||
*/ | ||
closeConnection() { | ||
clearTimeout(timeout); | ||
|
||
if (connection !== undefined) { | ||
connection.close(); | ||
} | ||
|
||
connection = undefined; | ||
}, | ||
|
||
/** | ||
* Stop reconnecting to WebSocket | ||
*/ | ||
stopReconnect() { | ||
reconnect = false; | ||
clearTimeout(timeout); | ||
}, | ||
|
||
postProcessFrame({ canvas, props }) { | ||
mappingContext.clearRect(0, 0, canvas.width, canvas.height); | ||
mappingContext.drawImage( | ||
canvas, | ||
0, | ||
0, | ||
canvas.width, | ||
canvas.height, | ||
0, | ||
0, | ||
props.mappingWidth, | ||
props.mappingHeight | ||
); | ||
|
||
const imageData = mappingContext.getImageData( | ||
0, | ||
0, | ||
props.mappingWidth, | ||
props.mappingHeight | ||
); | ||
const { data } = imageData; | ||
const arrayData = Array.from(data); | ||
const rgbArray = arrayData.filter((value, index) => (index + 1) % 4 !== 0); | ||
|
||
this.send(rgbArray); | ||
}, | ||
|
||
/** | ||
* Send data to WebSocket if connection is established | ||
* @param {Object} data | ||
*/ | ||
send(data) { | ||
// Connection is established | ||
if (connection !== undefined && connection.readyState === 1) { | ||
const message = { | ||
_type: "modV", | ||
colors: data | ||
}; | ||
|
||
const messageString = JSON.stringify(message, null, 2); | ||
|
||
// Send JSON message | ||
connection.send(messageString); | ||
} | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
<template> | ||
<Control | ||
@input="handleInput" | ||
:inputTitle="`${moduleName}: ${title}`" | ||
:activeProp="activeProp" | ||
:title="title" | ||
:value="value" | ||
/> | ||
</template> | ||
|
||
<script> | ||
import Control from "./Control"; | ||
export default { | ||
components: { | ||
Control | ||
}, | ||
props: { | ||
prop: { | ||
type: String, | ||
required: true | ||
}, | ||
id: { | ||
type: String, | ||
required: true | ||
} | ||
}, | ||
computed: { | ||
activeProp() { | ||
const { id, prop } = this; | ||
return this.$modV.store.state.modules.active[id].$props[prop]; | ||
}, | ||
type() { | ||
return this.activeProp.type; | ||
}, | ||
moduleName() { | ||
const { id } = this; | ||
return this.$modV.store.state.modules.active[id].meta.name; | ||
}, | ||
title() { | ||
return this.activeProp.label || this.prop; | ||
}, | ||
value() { | ||
const { id, prop, type } = this; | ||
const propData = this.$modV.store.state.modules.active[id].props[prop]; | ||
if (type === "tween") { | ||
return this.$modV.store.state.tweens.tweens[propData.id]; | ||
} | ||
return propData; | ||
} | ||
}, | ||
methods: { | ||
async handleInput(e) { | ||
const { prop, id: moduleId } = this; | ||
const data = e; | ||
try { | ||
await this.$modV.store.dispatch("modules/updateProp", { | ||
moduleId, | ||
prop, | ||
data | ||
}); | ||
} catch (e) { | ||
console.error(e.message); | ||
} | ||
} | ||
} | ||
}; | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
<template> | ||
<Control | ||
@input="handleInput" | ||
:inputTitle="`${pluginName}: ${title}`" | ||
:activeProp="activeProp" | ||
:title="title" | ||
:value="value" | ||
/> | ||
</template> | ||
|
||
<script> | ||
import Control from "./Control"; | ||
export default { | ||
components: { | ||
Control | ||
}, | ||
props: { | ||
prop: { | ||
type: String, | ||
required: true | ||
}, | ||
id: { | ||
type: String, | ||
required: true | ||
} | ||
}, | ||
computed: { | ||
plugin() { | ||
const { id } = this; | ||
return this.$modV.store.state.plugins.find(item => item.id === id); | ||
}, | ||
activeProp() { | ||
const { plugin, prop } = this; | ||
return plugin.props[prop]; | ||
}, | ||
type() { | ||
return this.activeProp.type; | ||
}, | ||
pluginName() { | ||
return this.plugin.name; | ||
}, | ||
title() { | ||
return this.activeProp.label || this.prop; | ||
}, | ||
value() { | ||
const { prop, type, plugin } = this; | ||
const propData = plugin.$props[prop]; | ||
if (type === "tween") { | ||
return this.$modV.store.state.tweens.tweens[propData.id]; | ||
} | ||
return propData; | ||
} | ||
}, | ||
methods: { | ||
async handleInput(e) { | ||
const { prop, id: pluginId } = this; | ||
const data = e; | ||
try { | ||
await this.$modV.store.dispatch("plugins/updateProp", { | ||
pluginId, | ||
prop, | ||
data | ||
}); | ||
} catch (e) { | ||
console.error(e.message); | ||
} | ||
} | ||
} | ||
}; | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
<template> | ||
<grid | ||
class="borders" | ||
v-infoView="{ title: iVTitle, body: iVBody, id: 'Plugins Panel' }" | ||
v-searchTerms="{ | ||
terms: ['plugin', 'plug in', 'addon', 'add-on'], | ||
title: 'Plugins', | ||
type: 'Panel' | ||
}" | ||
> | ||
<CollapsibleRow v-for="plugin in plugins" :key="plugin.id"> | ||
<template v-slot:label> | ||
{{ plugin.name }} | ||
</template> | ||
|
||
<template v-slot:body> | ||
<c span="1.."> | ||
<grid columns="4"> | ||
<c span="1.."> | ||
<grid columns="4"> | ||
<c span="1"> | ||
Enable | ||
</c> | ||
<c span="3"> | ||
<Checkbox | ||
@input="handleEnableInput(plugin.id)" | ||
class="light" | ||
/> | ||
</c> | ||
</grid> | ||
</c> | ||
|
||
<c | ||
span="1.." | ||
v-for="(prop, propKey) in plugin.$props" | ||
:key="propKey" | ||
> | ||
<PluginControl :id="plugin.id" :prop="propKey" /> | ||
</c> | ||
</grid> | ||
</c> | ||
</template> | ||
</CollapsibleRow> | ||
</grid> | ||
</template> | ||
|
||
<script> | ||
import CollapsibleRow from "./CollapsibleRow.vue"; | ||
import Checkbox from "./inputs/Checkbox.vue"; | ||
import PluginControl from "./PluginControl.vue"; | ||
export default { | ||
components: { | ||
CollapsibleRow, | ||
Checkbox, | ||
PluginControl | ||
}, | ||
data() { | ||
return { | ||
iVTitle: "Plugins", | ||
iVBody: "The Plugins panel lists all available Plugins." | ||
}; | ||
}, | ||
computed: { | ||
plugins() { | ||
return this.$modV.store.state.plugins; | ||
} | ||
}, | ||
methods: { | ||
handleEnableInput(pluginId) { | ||
const plugin = this.plugins.find(item => item.id === pluginId); | ||
if (!plugin) { | ||
return; | ||
} | ||
this.$modV.store.dispatch("plugins/setEnabled", { | ||
pluginId, | ||
enabled: !plugin.enabled | ||
}); | ||
} | ||
} | ||
}; | ||
</script> | ||
|
||
<style></style> |