-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
701 additions
and
13 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# Logs | ||
logs | ||
*.log | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
pnpm-debug.log* | ||
lerna-debug.log* | ||
|
||
node_modules | ||
dist | ||
dist-ssr | ||
*.local | ||
|
||
# Editor directories and files | ||
.vscode/* | ||
!.vscode/extensions.json | ||
.idea | ||
.DS_Store | ||
*.suo | ||
*.ntvs* | ||
*.njsproj | ||
*.sln | ||
*.sw? |
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,13 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>Vite + TS</title> | ||
</head> | ||
<body> | ||
<div id="root"></div> | ||
<script type="module" src="/src/main.ts"></script> | ||
</body> | ||
</html> |
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,20 @@ | ||
{ | ||
"name": "paint", | ||
"private": true, | ||
"version": "0.0.0", | ||
"type": "module", | ||
"scripts": { | ||
"dev": "vite", | ||
"build": "tsc && vite build", | ||
"preview": "vite preview" | ||
}, | ||
"devDependencies": { | ||
"@types/tinycolor2": "^1.4.3", | ||
"typescript": "^4.9.3", | ||
"vite": "^4.2.0" | ||
}, | ||
"dependencies": { | ||
"manitas": "workspace:^1.0.0", | ||
"tinycolor2": "^1.6.0" | ||
} | ||
} |
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,122 @@ | ||
import { getNewAngle, rotatePoint, varyColour } from "./brushHelpers"; | ||
|
||
let currentAngle: number | null = null; | ||
let lastPoint: Point | null = null; | ||
let strokeWidth: number = 25; | ||
let varyBrightness: number = 5; | ||
let brush: Brush | null = null; | ||
|
||
function startStroke(point: Point, color: string) { | ||
currentAngle = null; | ||
brush = makeBrush(strokeWidth, color, varyBrightness); | ||
lastPoint = point; | ||
} | ||
|
||
function continueStroke(newPoint: Point, context: CanvasRenderingContext2D) { | ||
if (!lastPoint) { | ||
lastPoint = newPoint; | ||
return; | ||
} | ||
const newAngle = getNewAngle(lastPoint, newPoint, currentAngle); | ||
if (currentAngle === null) { | ||
currentAngle = newAngle % (Math.PI * 2); | ||
} | ||
if (brush !== null) { | ||
drawStroke( | ||
brush, | ||
lastPoint, | ||
newPoint, | ||
currentAngle, | ||
newAngle, | ||
strokeWidth, | ||
context | ||
); | ||
} | ||
currentAngle = newAngle % (Math.PI * 2); | ||
lastPoint = newPoint; | ||
} | ||
|
||
function makeBrush( | ||
strokeWidth: number, | ||
colour: string, | ||
varyBrightness: number | ||
) { | ||
const brush: Brush = []; | ||
const bristleCount = Math.round(strokeWidth / 3); | ||
const gap = strokeWidth / bristleCount; | ||
for (let i = 0; i < bristleCount; i++) { | ||
const distance = | ||
i === 0 ? 0 : gap * i + (Math.random() * gap) / 2 - gap / 2; | ||
brush.push({ | ||
distance, | ||
thickness: Math.random() * 2 + 2, | ||
colour: varyColour(colour, varyBrightness), | ||
}); | ||
} | ||
return brush; | ||
} | ||
|
||
const strokeBristle = ( | ||
origin: Point, | ||
destination: Point, | ||
bristle: Bristle, | ||
controlPoint: Point, | ||
context: CanvasRenderingContext2D | ||
) => { | ||
context.beginPath(); | ||
context.moveTo(origin[0], origin[1]); | ||
context.strokeStyle = bristle.colour; | ||
context.lineWidth = bristle.thickness; | ||
context.lineCap = "round"; | ||
context.lineJoin = "round"; | ||
context.shadowColor = bristle.colour; | ||
context.shadowBlur = bristle.thickness / 2; | ||
context.quadraticCurveTo( | ||
controlPoint[0], | ||
controlPoint[1], | ||
destination[0], | ||
destination[1] | ||
); | ||
context.lineTo(destination[0], destination[1]); | ||
context.stroke(); | ||
}; | ||
|
||
const drawStroke = ( | ||
bristles: Brush, | ||
origin: Point, | ||
destination: Point, | ||
oldAngle: number, | ||
newAngle: number, | ||
strokeWidth: number, | ||
context: CanvasRenderingContext2D | ||
) => { | ||
bristles.forEach((bristle) => { | ||
context.beginPath(); | ||
const bristleOrigin = rotatePoint( | ||
bristle.distance - strokeWidth / 2, | ||
oldAngle, | ||
origin | ||
); | ||
|
||
const bristleDestination = rotatePoint( | ||
bristle.distance - strokeWidth / 2, | ||
newAngle, | ||
destination | ||
); | ||
const controlPoint = rotatePoint( | ||
bristle.distance - strokeWidth / 2, | ||
newAngle, | ||
origin | ||
); | ||
|
||
strokeBristle( | ||
bristleOrigin, | ||
bristleDestination, | ||
bristle, | ||
controlPoint, | ||
context | ||
); | ||
}); | ||
}; | ||
|
||
export { startStroke, continueStroke }; |
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,43 @@ | ||
import tinycolor from "tinycolor2"; | ||
|
||
export function varyColour(sourceColour: string, varyBrightness: number) { | ||
const amount = Math.round(Math.random() * varyBrightness); | ||
const alpha = 0.8 - Math.random() / 4; | ||
const colour = tinycolor(sourceColour); | ||
const varied = colour.darken(amount - varyBrightness / 2).setAlpha(alpha); | ||
return varied.toPercentageRgbString(); | ||
} | ||
|
||
export const rotatePoint = ( | ||
distance: number, | ||
angle: number, | ||
origin: Point | ||
): Point => [ | ||
origin[0] + distance * Math.cos(angle), | ||
origin[1] + distance * Math.sin(angle), | ||
]; | ||
|
||
export const getBearing = (origin: Point, destination: Point) => | ||
(Math.atan2(destination[1] - origin[1], destination[0] - origin[0]) - | ||
Math.PI / 2) % | ||
(Math.PI * 2); | ||
|
||
export const getNewAngle = ( | ||
origin: Point, | ||
destination: Point, | ||
oldAngle: number | null | ||
) => { | ||
const bearing = getBearing(origin, destination); | ||
if (oldAngle === null) { | ||
return bearing; | ||
} | ||
return oldAngle - angleDiff(oldAngle, bearing); | ||
}; | ||
|
||
export const angleDiff = (angleA: number, angleB: number) => { | ||
const twoPi = Math.PI * 2; | ||
const diff = | ||
((angleA - (angleB > 0 ? angleB : angleB + twoPi) + Math.PI) % twoPi) - | ||
Math.PI; | ||
return diff < -Math.PI ? diff + twoPi : diff; | ||
}; |
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,34 @@ | ||
import { setupPaint } from "./paint"; | ||
import "./style.css"; | ||
import typescriptLogo from "./typescript.svg"; | ||
import viteLogo from "/vite.svg"; | ||
|
||
document.querySelector<HTMLDivElement>("#root")!.innerHTML = ` | ||
<center> | ||
<h1>Manitas React Example</h1> | ||
<p> | ||
(This example is much better in desktop, not ready for mobile yet!) | ||
</p> | ||
</center> | ||
<div class="center"> | ||
<video | ||
id="webcam" | ||
autoPlay | ||
playsInline | ||
style="height: 960px; width: 1280px" | ||
class="cam" | ||
> | ||
</video> | ||
<canvas | ||
id="canvas" | ||
class="canvas" | ||
width=1280 height=960 | ||
> | ||
</canvas> | ||
</div> | ||
<div> | ||
`; | ||
|
||
setupPaint(document.querySelector<HTMLCanvasElement>("#canvas")); | ||
|
||
//setupCounter(document.querySelector<HTMLButtonElement>("#counter")!); |
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,65 @@ | ||
import { init, AirfingerEvent } from "manitas"; | ||
import tinycolor from "tinycolor2"; | ||
import { continueStroke, startStroke } from "./brush"; | ||
|
||
const WIDTH = 1280; | ||
const HEIGHT = 960; | ||
|
||
export function setupPaint(canvasElement: HTMLCanvasElement | null) { | ||
if (!canvasElement) { | ||
console.warn("Unable to find canvas element"); | ||
return; | ||
} | ||
init({ delegate: "GPU" }); | ||
const canvasCtx = canvasElement.getContext("2d"); | ||
if (!canvasCtx) { | ||
console.warn("Unable to get canvas context"); | ||
} | ||
const document: Document = window.document; | ||
document.addEventListener("airfingerstart", airfingerStart); | ||
document.addEventListener("airfingermove", airfingerMove(canvasCtx)); | ||
document.addEventListener("airfingerend", airfingerEnd); | ||
setupPallete(canvasCtx as CanvasRenderingContext2D); | ||
} | ||
|
||
function airfingerStart(event: Event) { | ||
const detail = (event as AirfingerEvent).detail; | ||
const { x, y } = detail.airpoint; | ||
startStroke([x * WIDTH, y * HEIGHT], colors[colorIdx]); | ||
} | ||
|
||
function airfingerMove(canvasCtx: CanvasRenderingContext2D) { | ||
return function (event: Event) { | ||
const detail = (event as AirfingerEvent).detail; | ||
const { x, y } = detail.airpoint; | ||
continueStroke([x * WIDTH, y * HEIGHT], canvasCtx); | ||
}; | ||
} | ||
|
||
function airfingerEnd(event: Event) { | ||
const detail = (event as AirfingerEvent).detail; | ||
//console.log(detail); | ||
} | ||
|
||
let colorIdx = 0; | ||
|
||
function setupPallete(canvasCtx: CanvasRenderingContext2D) { | ||
canvasCtx.clearRect(5, 15, 160, 480); | ||
colors.forEach((color, idx) => { | ||
console.log(idx); | ||
const colorPallete = tinycolor(color); | ||
canvasCtx.fillStyle = | ||
idx === colorIdx | ||
? colorPallete.setAlpha(0.9).toString() | ||
: colorPallete.setAlpha(0.5).toString(); | ||
console.log(canvasCtx.fillStyle); | ||
canvasCtx.fillRect(10, 120 * idx + 30, 150, 100); | ||
}); | ||
} | ||
|
||
const colors = [ | ||
"rgb(0, 161, 157, 0.5)", | ||
"rgb(255, 248, 229, 0.5)", | ||
"rgb(255, 179, 68,0.5)", | ||
"rgb(224, 93, 93,0.5)", | ||
]; |
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,58 @@ | ||
.deck { | ||
position: absolute; | ||
width: 300px; | ||
height: 200px; | ||
will-change: transform; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
touch-action: none; | ||
} | ||
|
||
.deck > div { | ||
background-size: auto 85%; | ||
background-repeat: no-repeat; | ||
background-position: center center; | ||
width: 270px; | ||
height: 202px; | ||
will-change: transform; | ||
opacity: 0.9; | ||
border-radius: 10px; | ||
box-shadow: 0 12.5px 100px -10px rgba(50, 50, 73, 0.4), 0 10px 10px -10px rgba(50, 50, 73, 0.3); | ||
position: absolute; | ||
} | ||
|
||
.deck > div > video { | ||
background-size: auto 85%; | ||
background-repeat: no-repeat; | ||
background-position: center center; | ||
width: 100%; | ||
height: 100%; | ||
will-change: transform; | ||
border-radius: 10px; | ||
box-shadow: 0 12.5px 100px -10px rgba(50, 50, 73, 0.4), 0 10px 10px -10px rgba(50, 50, 73, 0.3); | ||
position: absolute; | ||
} | ||
|
||
/*.cam { | ||
/* position: absolute; | ||
/*}*/ | ||
|
||
.container { | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
position: relative; | ||
} | ||
.center { | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
position: relative; | ||
overflow: hidden; | ||
position: relative; | ||
} | ||
|
||
.canvas { | ||
position: absolute; | ||
} |
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,8 @@ | ||
declare type Point = [number, number]; | ||
|
||
declare interface Bristle { | ||
distance: number; | ||
thickness: number; | ||
colour: string; | ||
} | ||
declare type Brush = Bristle[]; |
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 @@ | ||
/// <reference types="vite/client" /> |
Oops, something went wrong.