Skip to content

Commit

Permalink
Add more options for litho
Browse files Browse the repository at this point in the history
  • Loading branch information
johnathanweidman committed Jan 29, 2024
1 parent 1f4ff48 commit 04c6c7f
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 67 deletions.
13 changes: 11 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"p5": "^1.9.0",
"p5-svelte": "^3.1.2",
"svelte-awesome-color-picker": "^3.0.4",
"svelte-heros-v2": "^1.2.0",
"three": "^0.160.1"
}
}
13 changes: 9 additions & 4 deletions src/components/ColorPicker.svelte
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
<script>
import ColorPicker from 'svelte-awesome-color-picker';
import * as Icon from 'svelte-heros-v2';
export let color = [null, null]
export let min;
export let max
export let remove = ()=>{};
export let removable = true;
export let min = '1'
export let max = '25'
$: console.log(color);
</script>

<div>
{#if removable}
<button title="Remove" on:click={()=>remove(color)} ><Icon.Minus size="18" class="inline-flex" /></button>
{/if}
<ColorPicker bind:hex = {color[0]} nullable /><br>
<label>Translucency: </label><br>
<input type = 'number' bind:value = {color[1]}/><br>
<label>layer: </label><br>
<input type = 'number' {min} {max} bind:value = {color[2]}/><br>
<input type = 'number' min={min} max={max} value = {color[2]}/><br>
</div>


150 changes: 89 additions & 61 deletions src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,58 @@
import mixbox from 'mixbox';
import {imageToMesh, saveSTL } from '../lib/Litho.js';
import ColorPicker from '../components/ColorPicker.svelte';
import * as Icon from 'svelte-heros-v2';
/**
* @type {string | ArrayBuffer | null}
*/
let currentFile = null;
let redrawFn = ()=>{};
let final = null;
let base_color = ['black'];
let stlWidth = 100
let stlHeight = 100
let base_color = ['black', 1];
let thickness = 2;
let layerHeight = .08;
let stlMinThickness = .32;
let layers = Math.round(thickness/layerHeight);
let use_gray = true;
/**
* @type {{ resize: (arg0: number, arg1: number) => void; loadPixels: () => void; width: number; height: any; pixels: any[]; } | null}
*/
let img = null;
/**
* @type {{ resize: (arg0: number, arg1: number) => void; loadPixels: () => void; pixels: string | any[]; updatePixels: () => void; canvas: any; } | null}
*/
let copy = null;
let range = use_gray ? 255 : 360 // rgb vs hue ranges
let final_w = 600 // scaling factor for images, use 2 or more if it takes too long to process
let layer = 1
let total = Date.now()
/**
/**
* @type {null}
*/
let mesh = null;
let lithoSettings = {
litophaneWidth: 100,
thickness: 2,
minThickness: .32
}
let layers = Math.round(lithoSettings.thickness/layerHeight);
let subd = Number(range)/Number(layers)
let colors = [
['red', .2, 4],
['yellow', .2 , 7],
['white', .8, 18],
]
const remove = (/** @type {(string | number)[]} */ color) =>{
colors = colors.filter((c)=> color !== c)
}
const addColor = ()=>{
colors = [ ...colors, []];
}
$: {
// @ts-ignore
redrawFn(currentFile);
layers = Math.round(thickness/layerHeight);
}
Expand All @@ -45,93 +75,85 @@
reader.readAsDataURL(file);
}
const sketch = (p5) => {
let img = null;
let copy = null;
let use_gray = true;
let range = use_gray ? 255 : 360 // rgb vs hue ranges
const sketch = (p5) => {
let current_value = null;
let orig_color = p5.color(base_color[0])
let current_color = base_color
let final_w = 600 // scaling factor for images, use 2 or more if it takes too long to process
// let layers = 21 // stl model layers
let layer = 1
let total = Date.now()
let subd = null;
let orig_color = null;
let result;
let current_value = null;
let current_color = null;
let resetColors = () =>{
orig_color = p5.color(base_color[0])
current_color = base_color
}
const preload = () => {
p5.preload = () => {
img = p5.loadImage(currentFile)
copy = p5.loadImage(currentFile)
}
function setup(){
subd = parseInt(range/layers)
img.resize(final_w, 0);
copy.resize(final_w, 0);
img.loadPixels()
copy.loadPixels()
p5.setup = () => {
if(img && copy){
img.resize(final_w, 0);
copy.resize(final_w, 0);
img.loadPixels()
copy.loadPixels()
}
p5.createCanvas(img.width*2 + 200, img.height, 'WEBGL');
redrawFn = () => {
layer = 1;
p5.blend(0, 0, final_w, img.height, 0, 0, final_w, img.height, 'REPLACE')
p5.redraw()
p5.loop()
redrawFn = (/** @type {any} */ file) => {
if(file){
layer = 1;
p5.blend(0, 0, final_w, img.height, 0, 0, final_w, img.height, 'REPLACE')
resetColors();
p5.redraw()
p5.loop()
}
}
}
const draw = () => {
p5.draw = () => {
if(layer <= layers) {
let thresh = parseInt(subd * (layer - 1)) // threshold to change pixels\
let thresh = subd * (layer - 1) // threshold to change pixels\
for( let i=0 ; i < copy.pixels.length; i+=4){
if (img.pixels[i + 3] == 0) continue; // skip on transparent pixels for png files
if (layer == 1) { // the origin color for the color mix. After the first layer, it is the current image's color
orig_color = p5.color([base_color[0]])
current_color = [base_color[0], 1 ]
} else {
orig_color = p5.color(copy.pixels[i], copy.pixels[i + 1], copy.pixels[i + 2])
if (img?.pixels[i + 3] == 0) continue; // skip on transparent pixels for png files
if (layer !== 1) { // the origin color for the color mix. After the first layer, it is the current image's color
orig_color = p5.color(copy?.pixels[i], copy?.pixels[i + 1], copy?.pixels[i + 2])
}
if (use_gray) {
current_value = (img.pixels[i] + img.pixels[i + 1] + img.pixels[i + 2])/3
current_value = (img?.pixels[i] + img?.pixels[i + 1] + img?.pixels[i + 2])/3
} else {
let c = p5.color(img.pixels[i], img.pixels[i + 1], img.pixels[i + 2])
let c = p5.color(img?.pixels[i], img?.pixels[i + 1], img?.pixels[i + 2])
current_value = p5.hue(c)
}
if(current_value >= thresh){
current_color = colors.find(color => color[2] == layer) || current_color
result = mixbox.lerp(orig_color.levels, p5.color(current_color[0]).levels, current_color[1]) // color mix using mixbox for accurate pigment mixing results
current_color = colors.find(color => color[2] == layer) || current_color || base_color
let result = mixbox.lerp(orig_color.levels, p5.color(current_color[0]).levels, current_color[1]) // color mix using mixbox for accurate pigment mixing results
// @ts-ignore
copy.pixels[i + 0] = result[0]
// @ts-ignore
copy.pixels[i + 1] = result[1]
// @ts-ignore
copy.pixels[i + 2] = result[2]
}
}
copy.updatePixels()
copy?.updatePixels()
p5.image(copy, 0, 0)
p5.image(img, img.width + 20, 0)
layer++
} else {
console.log("Took:" + (Date.now() - total)/1000.0 + "s")
final = copy.canvas;
mesh = imageToMesh(final)
mesh = imageToMesh(final, lithoSettings)
p5.noLoop()
}
}
p5.preload = preload;
p5.setup = setup;
p5.draw = draw;
};
</script>
<style>
Expand Down Expand Up @@ -159,14 +181,20 @@
<label for="my-drawer-4" aria-label="close sidebar" class="drawer-overlay"></label>
<ul class="menu p-4 w-120 min-h-full bg-base-200 text-base-content">
<input on:change={handleInput} accept="image/png, image/jpeg" id="avatar" name="avatar" type="file" />
<li><ColorPicker bind:color= {base_color}></ColorPicker></li>
<li><ColorPicker min={1} max={1} removable={false} bind:color= {base_color}></ColorPicker></li>
{#each colors as color}
<li><ColorPicker bind:color= {color}></ColorPicker></li>
<li><ColorPicker min={'1'}, max={layers.toString()} {remove} bind:color= {color}></ColorPicker></li>
{/each}
<li>
<label class="label cursor-pointer">
<span class="label-text">Use Grey:</span>
<input type="checkbox" class="toggle" bind:checked={use_gray} />
</label></li>
<li><button title="Add" on:click={addColor} ><Icon.Plus size="18" class="inline-flex" />Add Color</button></li>
<li><button on:click={redrawFn} class="btn">Redraw</button></li>
<li><lable>Width: </lable><input bind:value={stlWidth}></li>
<li><lable>Height: </lable><input bind:value={stlHeight}></li>
<li><lable>thickness: </lable><input bind:value={thickness}></li>
<li><lable>Width: </lable><input bind:value={lithoSettings.litophaneWidth}></li>
<li><lable>Maximum depth: </lable><input bind:value={lithoSettings.thickness}></li>
<li><lable>Minimum depth: </lable><input bind:value={lithoSettings.minThickness}></li>
<li><lable>layerHeight: </lable><input bind:value={layerHeight}></li>
<li><lable>Layers: </lable><input disabled value={layers}></li>
<li><button on:click={()=>saveSTL(mesh)} disabled = {!mesh} class="btn">download STL</button></li>
Expand Down

0 comments on commit 04c6c7f

Please sign in to comment.