If you plan to laser cut the base instead of 3D printing, you'll need a 2D drawing of the plate. that you can open in Inkscape or Illustrator and send to the cutter.
+
Please ensure the line labeled 1 cm is really 1 cm long before sending the file. Afterwards you can safely delete it.
+
+ {/if}
{/if}
{/each}
diff --git a/src/connectors.md b/src/connectors.md
new file mode 100644
index 0000000..4393c4d
--- /dev/null
+++ b/src/connectors.md
@@ -0,0 +1,22 @@
+List of External Holders for the Dactyl
+====
+
+While I personally don't use external holders, they are a popular way to securely fasten a microcontroller and connector to your keyboard, especially if you don't have access to a drill to widen the holes in the model.
+
+If you're using one of these, make sure to set the connector type to *anything* but RJ9.
+
+| Image | Board | Model Downloads | Author |
+|-----------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------|-----------------------------------------------------------------------|------------------------------|
+| | Pro Micro (micro USB) | [[left (mesh fixed)][pm-leftf]] [[left][pm-left]] [[right][pm-right]] | [Blue Ye (@yejianfengblue )] |
+| | Pro Micro (USB-C) | [[left][pmc-left]] [[right][pmc-right]] | [Blue Ye (@yejianfengblue )] |
+| | Elite-C | [[right][ec-right]] | Unknown |
+
+[pm-leftf]: https://github.com/yejianfengblue/dactyl-generator-demo/blob/main/stl/promicro-holder-v3-left-mesh-fixed.stl
+[pm-left]: https://github.com/yejianfengblue/dactyl-generator-demo/blob/main/stl/promicro-holder-v3-left.stl
+[pm-right]: https://github.com/yejianfengblue/dactyl-generator-demo/blob/main/stl/promicro-holder-v3-right.stl
+[pmc-left]: https://github.com/yejianfengblue/dactyl-generator-demo/blob/main/stl/promicro-holder-typec-untested-left.stl
+[pmc-right]: https://github.com/yejianfengblue/dactyl-generator-demo/blob/main/stl/promicro-holder-typec-untested-right.stl
+[Blue Ye (@yejianfengblue )]: https://github.com/yejianfengblue
+[ec-right]: https://web.archive.org/web/20220607031927/https://dactyl.siskam.link/loligagger-external-holder-elite-c-v1.stl
+
+If you found one that isn't listed here, please submit a pull request! I'd like to make this list as complete as possible.
diff --git a/src/lib/SupportDialog.svelte b/src/lib/SupportDialog.svelte
index a346ea1..e117878 100644
--- a/src/lib/SupportDialog.svelte
+++ b/src/lib/SupportDialog.svelte
@@ -1,5 +1,5 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/worker/SVGExporter.ts b/src/worker/SVGExporter.ts
new file mode 100644
index 0000000..7c317ba
--- /dev/null
+++ b/src/worker/SVGExporter.ts
@@ -0,0 +1,85 @@
+/**
+ * Export a planar mesh to SVG.
+ *
+ * The formats are very different (a mesh is made of tesselated triangles, whereas SVG is a single face),
+ * so some preprocessing needs to be done.
+ * 1) The mesh is filtered so that only faces on the z plane are exported
+ * 2) The triangles of the mesh are combined into polygons
+ * 3) The polygons are written out to the SVG file.
+ */
+
+import type { Mesh } from 'manifold-3d';
+
+interface Options {
+ margin?: number
+ color?: string
+}
+
+export default function svgExport(mesh: Mesh, options: Options) {
+ const margin = options?.margin ?? 10
+ const color = options?.color ?? "#8080F7"
+
+ const flatFaces: [number, number, number][] = []
+ for (let i = 0; i < mesh.triVerts.length; i+=3) {
+ // a, b, and c are the points on face
+ const [a, b, c] = mesh.triVerts.slice(i, i+3)
+ // If the z coordinates of all points are 0, add it.
+ if (mesh.vertProperties[a*3+2] == 0 &&
+ mesh.vertProperties[b*3+2] == 0 &&
+ mesh.vertProperties[c*3+2] == 0) {
+ flatFaces.push([a, b, c])
+ }
+ }
+
+ const boundary = new Set()
+ for (const tri of flatFaces) {
+ for (const [e0, e1] of [[tri[0], tri[1]], [tri[1], tri[2]], [tri[2], tri[0]]]) {
+ if (boundary.has(e1 + ',' + e0)) {
+ boundary.delete(e1 + ',' + e0)
+ } else {
+ boundary.add(e0 + ',' + e1)
+ }
+ }
+ }
+
+ // To process the boundary, I create a queue of edges to their next edge (this map).
+ const next = new Map()
+ for (const b of boundary) {
+ const [e0, e1] = b.split(',').map(v => Number(v))
+ next.set(e0, e1)
+ }
+
+ let minX = 0
+ let maxX = 0
+ let minY = 0
+ let maxY = 0
+
+ const paths: string[] = []
+ // Extract a vertex in the boundary, follow the next edges until there is no more vertex, then repeat.
+ while (next.size > 0) {
+ let vertex = next.keys().next().value
+ let path = ''
+ while (next.has(vertex)) {
+ // Flip the y to preserve how things look in the preview
+ const [x, y] = [mesh.vertProperties[vertex*3], -mesh.vertProperties[vertex*3+1]]
+ path += `${path.length==0 ? 'M' : 'L'}${x},${y}`
+ minX = Math.min(minX, x-margin)
+ maxX = Math.max(maxX, x+margin)
+ minY = Math.min(minY, y-margin)
+ maxY = Math.max(maxY, y+margin)
+ const newVertex = next.get(vertex)
+ next.delete(vertex)
+ vertex = newVertex
+ }
+ paths.push(path + 'Z')
+ }
+
+ return `
+`
+}
diff --git a/src/worker/index.ts b/src/worker/index.ts
index 647bee5..48d14e6 100644
--- a/src/worker/index.ts
+++ b/src/worker/index.ts
@@ -7,6 +7,7 @@ import createGC, { type GC } from './gc'
import scadWasmUrl from '../assets/openscad.wasm?url'
import stlExport from './STLExporter'
import { supportManifold } from './supports'
+import svgExport from "./SVGExporter.js"
(Module as (a: any) => Promise)({
locateFile: () => manifoldWasmUrl,
@@ -29,6 +30,7 @@ function main(manifold: ManifoldStatic) {
switch (type) {
case 'csg': return generateCSG(data, modeling, cleanup)
case 'stl': return generateSTL(data, modeling, cleanup)
+ case 'svg': return generateSVG(data, modeling, cleanup)
case 'scad': return generateSCAD(data)
case 'scadstl': return generateSCAD_STL(data)
}
@@ -58,6 +60,16 @@ function generateSTL(config: any, modeling: Modeling, cleanup: GC) {
}
}
+function generateSVG(config: any, modeling: Modeling, cleanup: GC) {
+ try {
+ const mesh: Mesh = Dactyl.generateManifold(config, modeling).getMesh()
+ message('svg', svgExport(mesh))
+ cleanup()
+ } catch (e) {
+ console.error(e)
+ }
+}
+
function generateSCAD(config: any) {
message('scad', Dactyl.generateSCAD(config))
}