image-vectorizer
Version:
Potrace in Javascript, for NodeJS and Browser
307 lines (242 loc) • 5.66 kB
JavaScript
import Point from "./types/Point.js"
var attrRegexps = {}
function getAttrRegexp(attrName) {
if (attrRegexps[attrName]) {
return attrRegexps[attrName]
}
attrRegexps[attrName] = new RegExp(" " + attrName + '="((?:\\\\(?=")"|[^"])+)"', "i")
return attrRegexps[attrName]
}
function setHtmlAttribute(html, attrName, value) {
var attr = " " + attrName + '="' + value + '"'
if (html.indexOf(" " + attrName + '="') === -1) {
html = html.replace(/<[a-z]+/i, function (beginning) {
return beginning + attr
})
} else {
html = html.replace(getAttrRegexp(attrName), attr)
}
return html
}
function fixed(number) {
return number.toFixed(3).replace(".000", "")
}
function mod(a, n) {
return a >= n ? a % n : a >= 0 ? a : n - 1 - ((-1 - a) % n)
}
function xprod(p1, p2) {
return p1.x * p2.y - p1.y * p2.x
}
function cyclic(a, b, c) {
if (a <= c) {
return a <= b && b < c
} else {
return a <= b || b < c
}
}
function sign(i) {
return i > 0 ? 1 : i < 0 ? -1 : 0
}
function quadform(Q, w) {
var v = new Array(3),
i,
j,
sum
v[0] = w.x
v[1] = w.y
v[2] = 1
sum = 0.0
for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++) {
sum += v[i] * Q.at(i, j) * v[j]
}
}
return sum
}
function interval(lambda, a, b) {
var res = new Point()
res.x = a.x + lambda * (b.x - a.x)
res.y = a.y + lambda * (b.y - a.y)
return res
}
function dorth_infty(p0, p2) {
var r = new Point()
r.y = sign(p2.x - p0.x)
r.x = -sign(p2.y - p0.y)
return r
}
function ddenom(p0, p2) {
var r = dorth_infty(p0, p2)
return r.y * (p2.x - p0.x) - r.x * (p2.y - p0.y)
}
function dpara(p0, p1, p2) {
var x1, y1, x2, y2
x1 = p1.x - p0.x
y1 = p1.y - p0.y
x2 = p2.x - p0.x
y2 = p2.y - p0.y
return x1 * y2 - x2 * y1
}
function cprod(p0, p1, p2, p3) {
var x1, y1, x2, y2
x1 = p1.x - p0.x
y1 = p1.y - p0.y
x2 = p3.x - p2.x
y2 = p3.y - p2.y
return x1 * y2 - x2 * y1
}
function iprod(p0, p1, p2) {
var x1, y1, x2, y2
x1 = p1.x - p0.x
y1 = p1.y - p0.y
x2 = p2.x - p0.x
y2 = p2.y - p0.y
return x1 * x2 + y1 * y2
}
function iprod1(p0, p1, p2, p3) {
var x1, y1, x2, y2
x1 = p1.x - p0.x
y1 = p1.y - p0.y
x2 = p3.x - p2.x
y2 = p3.y - p2.y
return x1 * x2 + y1 * y2
}
function ddist(p, q) {
return Math.sqrt((p.x - q.x) * (p.x - q.x) + (p.y - q.y) * (p.y - q.y))
}
export default {
luminance: function (r, g, b) {
return Math.round(0.2126 * r + 0.7153 * g + 0.0721 * b)
},
between: function (val, min, max) {
return val >= min && val <= max
},
clamp: function (val, min, max) {
return Math.min(max, Math.max(min, val))
},
isNumber: function (val) {
return typeof val === "number"
},
setHtmlAttr: setHtmlAttribute,
/**
* Generates path instructions for given curve
*
* @param {Curve} curve
* @param {Number} [scale]
* @returns {string}
*/
renderCurve: function (curve, scale) {
scale = scale || { x: 1, y: 1 }
var startingPoint = curve.c[(curve.n - 1) * 3 + 2]
var path = ["M " + fixed(startingPoint.x * scale.x) + " " + fixed(startingPoint.y * scale.y)]
curve.tag.forEach(function (tag, i) {
var i3 = i * 3
var p0 = curve.c[i3]
var p1 = curve.c[i3 + 1]
var p2 = curve.c[i3 + 2]
if (tag === "CURVE") {
path.push(
"C " +
fixed(p0.x * scale.x) +
" " +
fixed(p0.y * scale.y) +
", " +
fixed(p1.x * scale.x) +
" " +
fixed(p1.y * scale.y) +
", " +
fixed(p2.x * scale.x) +
" " +
fixed(p2.y * scale.y)
)
} else if (tag === "CORNER") {
path.push(
"L " +
fixed(p1.x * scale.x) +
" " +
fixed(p1.y * scale.y) +
" " +
fixed(p2.x * scale.x) +
" " +
fixed(p2.y * scale.y)
)
}
})
return path.join(" ")
},
bezier: function bezier(t, p0, p1, p2, p3) {
var s = 1 - t,
res = new Point()
res.x = s * s * s * p0.x + 3 * (s * s * t) * p1.x + 3 * (t * t * s) * p2.x + t * t * t * p3.x
res.y = s * s * s * p0.y + 3 * (s * s * t) * p1.y + 3 * (t * t * s) * p2.y + t * t * t * p3.y
return res
},
tangent: function tangent(p0, p1, p2, p3, q0, q1) {
var A, B, C, a, b, c, d, s, r1, r2
A = cprod(p0, p1, q0, q1)
B = cprod(p1, p2, q0, q1)
C = cprod(p2, p3, q0, q1)
a = A - 2 * B + C
b = -2 * A + 2 * B
c = A
d = b * b - 4 * a * c
if (a === 0 || d < 0) {
return -1.0
}
s = Math.sqrt(d)
r1 = (-b + s) / (2 * a)
r2 = (-b - s) / (2 * a)
if (r1 >= 0 && r1 <= 1) {
return r1
} else if (r2 >= 0 && r2 <= 1) {
return r2
} else {
return -1.0
}
},
getImageData: async function (imageSrc, callback = null) {
return new Promise((resolve, reject) => {
// Create an image object
const img = new Image()
img.crossOrigin = "anonymous" // Handle CORS if needed
img.src = imageSrc
img.onload = () => {
// Create a canvas dynamically
const canvas = document.createElement("canvas")
const ctx = canvas.getContext("2d")
// Set canvas size to match the image
canvas.width = img.width
canvas.height = img.height
console.log(img.width, img.height)
// Draw the image on the canvas
ctx.drawImage(img, 0, 0)
// Get image data
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
// Return image data
if (callback) {
callback(null, imageData)
}
resolve(imageData)
}
img.onerror = (err) => {
if (callback) {
callback(err, null)
}
reject(err)
}
})
},
mod: mod,
xprod: xprod,
cyclic: cyclic,
sign: sign,
quadform: quadform,
interval: interval,
dorth_infty: dorth_infty,
ddenom: ddenom,
dpara: dpara,
cprod: cprod,
iprod: iprod,
iprod1: iprod1,
ddist: ddist
}