Skip to content

Commit

Permalink
Merge pull request #302 from jeffeb3/feature/237-program-code-effect
Browse files Browse the repository at this point in the history
annotated exports
  • Loading branch information
jeffeb3 authored Oct 24, 2024
2 parents ae0ee62 + dec968b commit c05f44b
Show file tree
Hide file tree
Showing 10 changed files with 355 additions and 152 deletions.
30 changes: 25 additions & 5 deletions src/common/geometry.js
Original file line number Diff line number Diff line change
Expand Up @@ -366,10 +366,14 @@ export const downsample = (vertices, tolerance = 0.0001) => {
}

// Convert x,y vertices to theta, rho vertices
export const toThetaRho = (subsampledVertices, maxRadius, rhoMax) => {
export const toThetaRho = (
subsampledVertices,
maxRadius,
rhoMax,
previousTheta = 0,
previousRawTheta = 0,
) => {
let vertices = []
let previousTheta = 0
let previousRawTheta = 0

// Normalize the radius
if (rhoMax < 0) {
Expand All @@ -389,6 +393,7 @@ export const toThetaRho = (subsampledVertices, maxRadius, rhoMax) => {
subsampledVertices[next].x,
subsampledVertices[next].y,
)

// Convert to [0, 2pi]
rawTheta = (rawTheta + 2.0 * Math.PI) % (2.0 * Math.PI)

Expand All @@ -407,7 +412,14 @@ export const toThetaRho = (subsampledVertices, maxRadius, rhoMax) => {

previousRawTheta = rawTheta
previousTheta = theta
vertices.push(new Victor(theta, rho))

const vertex = new Victor(theta, rho)

// small hack to preserve these values
vertex.theta = theta
vertex.rawTheta = rawTheta

vertices.push(vertex)
}

return vertices
Expand Down Expand Up @@ -462,7 +474,15 @@ export const toScaraGcode = (vertices, unitsPerCircle) => {
const x = (unitsPerCircle * m1) / (2 * Math.PI)
const y = (unitsPerCircle * m2) / (2 * Math.PI)

return new Victor(x, y)
// propagate theta if it exists
const vertex = new Victor(x, y)

if (thetaRho.theta) {
vertex.theta = thetaRho.theta
vertex.rawTheta = thetaRho.rawTheta
}

return vertex
})
}

Expand Down
3 changes: 3 additions & 0 deletions src/features/effects/EffectEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ const EffectEditor = ({ id }) => {
</Col>
</Row>
)}
{model.description && (
<div className="mt-3 mb-2 bg-light p-4">{model.description}</div>
)}
</div>
)
}
Expand Down
51 changes: 51 additions & 0 deletions src/features/effects/ProgramCode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import Effect from "./Effect"

const options = {
programCodePre: {
title: "Start code",
type: "textarea",
},
programCodePost: {
title: "End code",
type: "textarea",
},
}

export default class ProgramCode extends Effect {
constructor() {
super("programCode")
this.label = "Program code"
this.description =
"When exporting the pattern to a file, the provided program code is added before and/or after this layer is rendered."
}

canChangeSize(state) {
return false
}

canRotate(state) {
return false
}

canMove(state) {
return false
}

getInitialState() {
return {
...super.getInitialState(),
...{
programCodePre: "",
programCodePost: "",
},
}
}

getVertices(effect, layer, vertices) {
return vertices
}

getOptions() {
return options
}
}
2 changes: 2 additions & 0 deletions src/features/effects/effectFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Fisheye from "./Fisheye"
import Loop from "./Loop"
import Mask from "./Mask"
import Noise from "./noise/Noise"
import ProgramCode from "./ProgramCode"
import Track from "./Track"
import Transformer from "./Transformer"
import Warp from "./Warp"
Expand All @@ -13,6 +14,7 @@ export const effectFactory = {
fisheye: Fisheye,
fineTuning: FineTuning,
mask: Mask,
programCode: ProgramCode,
noise: Noise,
track: Track,
warp: Warp,
Expand Down
4 changes: 2 additions & 2 deletions src/features/export/ExportDownloader.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { downloadFile } from "@/common/util"
import DropdownOption from "@/components/DropdownOption"
import InputOption from "@/components/InputOption"
import CheckboxOption from "@/components/CheckboxOption"
import { selectConnectedVertices } from "@/features/layers/layersSlice"
import { selectLayersForExport } from "@/features/layers/layersSlice"
import {
selectExporterState,
updateExporter,
Expand Down Expand Up @@ -84,7 +84,7 @@ const ExportDownloader = ({ showModal, toggleModal }) => {
Math.pow((machine.maxY - machine.minY) / 2, 2.0),
)
: machine.maxRadius,
vertices: useSelector(selectConnectedVertices),
layers: useSelector(selectLayersForExport),
}
const exporter = new exporters[fields.fileType](props)

Expand Down
173 changes: 131 additions & 42 deletions src/features/export/Exporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ export const exporterOptions = {
title: "Units per circle",
type: "number",
},
post: {
title: "Program end code",
pre: {
title: "Program start code",
type: "textarea",
isVisible: (exporter, state) => {
return state.fileType !== SVG
},
},
pre: {
title: "Program start code",
post: {
title: "Program end code",
type: "textarea",
isVisible: (exporter, state) => {
return state.fileType !== SVG
Expand All @@ -52,70 +52,139 @@ export const exporterOptions = {
export default class Exporter {
constructor(props) {
this.props = props
this.pre = props.pre
this.post = props.post
this.lines = []
this.indentLevel = 0
this.digits = 3
}

export() {
this.pre = this.props.pre
this.post = this.props.post
let vertices = this.props.vertices
this.layers = this.prepareLayers(this.props.layers)

const allVertices = this.layers.map((layer) => layer.vertices).flat()
this.computeStats(allVertices, [this])

if (this.props.reverse) {
vertices = vertices.reverse()
}
this.computeOutputVertices(vertices)
this.header()
this.startComments()
this.line()
this.keyValueLine("File name", "'" + this.props.fileName + "'")
this.keyValueLine("File type", this.props.fileType)
this.line()
this.endComments()
this.comment(() => {
this.line()
this.keyValueLine("File name", "'" + this.props.fileName + "'")
this.keyValueLine("File type", this.props.fileType)
this.line()
})

if (this.pre !== "") {
this.startComments()
this.line("BEGIN PRE")
this.endComments()
this.line(this.pre, this.pre !== "")
this.startComments()
this.line("END PRE")
this.endComments()
this.comment("BEGIN PRE")
this.line(this.pre, this.pre !== "", false)
this.comment("END PRE")
}

this.line()
this.exportCode(this.vertices)
this.exportCode()
this.line()

if (this.post !== "") {
this.startComments()
this.line("BEGIN POST")
this.endComments()
this.line(this.post, this.post !== "")
this.startComments()
this.line("END POST")
this.endComments()
this.comment("BEGIN POST")
this.line(this.post, this.post !== "", false)
this.comment("END POST")
}

this.footer()
this.line()

return this.lines
}

header() {
// default does nothing
// computes stats from vertices and replaces the corresponding stat variables within pre and
// post blocks
computeStats(vertices, objects) {
const values = this.collectStats(vertices)

Object.keys(values).forEach((variable) => {
objects.forEach((obj) => {
obj.pre = this.replaceVariable(obj.pre, variable, values[variable])
obj.post = this.replaceVariable(obj.post, variable, values[variable])
})
})
}

// given layers and connectors with vertices, transforms those vertices into an exportable format
prepareLayers(layers) {
layers = [...layers]

// reverse both the order of the layers and the vertices within them
if (this.props.reverse) {
layers = layers.reverse()
}

layers.forEach((layer, index) => {
let vertices = this.transformVertices(layer.vertices, index, layers)

if (this.props.reverse) vertices = vertices.reverse()
layer.vertices = vertices
})

return layers
}

exportCode() {
this.layers.forEach((layer) => {
const hasCode = layer.code && layer.code.length > 0

if (hasCode) {
this.computeStats(layer.vertices, layer.code)

this.line("")
this.actionComment(layer, "BEGIN PRE")

layer.code.forEach((block) => {
this.line(block.pre, block.pre !== "", false)
})
this.actionComment(layer, "END PRE")
}

this.line("")
this.actionComment(layer, "BEGIN")

layer.vertices.forEach((vertex) => {
this.line(this.code(vertex))
})

this.actionComment(layer, "END")

if (hasCode) {
this.line("")
this.actionComment(layer, "BEGIN POST")

layer.code.forEach((block) => {
this.line(block.post, block.post !== "", false)
})
this.actionComment(layer, "END POST")
}
})
}

// override to transform vertices for export
transformVertices(vertices) {
return vertices
}

footer() {
// default does nothing
// override to collect stats for use in PRE and POST blocks
collectStats() {
return {}
}

computeOutputVertices(vertices) {
// default does nothing
this.vertices = vertices
// override to export a header
header() {}

// override to export a footer
footer() {}

// override to provide code for a given vertex
code(vertex) {
throw "Override this method"
}

line(content = "", add = true) {
line(content = "", add = true, sanitize = true) {
if (add) {
let padding = ""
if (this.commenting) {
Expand All @@ -124,7 +193,10 @@ export default class Exporter {
padding += " "
}
}
this.lines.push(padding + this.sanitizeValue(content))

const preparedContent = sanitize ? this.sanitizeValue(content) : content

this.lines.push(padding + preparedContent)
}
}

Expand All @@ -148,6 +220,23 @@ export default class Exporter {
this.commenting = false
}

comment(fn) {
this.startComments()
typeof fn === "function" ? fn() : this.line(fn)
this.endComments()
}

actionComment(layer, action) {
const name = layer.name ? ` ${layer.name}` : ""
this.comment(`${action} ${layer.type}: ${layer.index}${name}`)
}

replaceVariable(str, variable, value) {
const regex = new RegExp(`{${variable}}`, "gi")

return str.replace(regex, value.toFixed(this.digits))
}

sanitizeValue(value) {
return value.replace("\n", " ")
}
Expand Down
Loading

0 comments on commit c05f44b

Please sign in to comment.