diff --git a/canvas/image-loading.js b/canvas/image-loading.js index 11def24..ab595d5 100644 --- a/canvas/image-loading.js +++ b/canvas/image-loading.js @@ -31,8 +31,18 @@ function setup() { setup(); +const { load_image_from_src, Image_Data_Sampler, Bitfield_Image_Sampler } = await import('../lib/image-functions.js'); -const { load_image_from_src, Image_Data_Sampler } = await import('../lib/image-functions.js'); +/* +//Convert const sampler = new Image_Data_Sampler(await load_image_from_src("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAFVJREFUOE9jZGBg+A/EZAMmsnVCNQ4DA1hAXvn/HxGOjIyMJAULC0gzsiZ0PiHTwC5AB8guQpdD52M1gJA3YK4E0SwgxZSEASjERlMieryQyKc4MwEA45UXJyonWOMAAAAASUVORK5CYII=")); - console.log(sampler.encode_as_bitfield()); +*/ + +//Load +const sampler = Bitfield_Image_Sampler.from_string('16,16,AAAAAAAAAAAAAA4AEQDx/xGgDqAAAAAAAAAAAAAAAAA='); +sampler.pixel_map((x, y, value) => { + C.beginPath(); + C.arc(x * 0.1 - sampler.width * .05, sampler.height * .05 - y * 0.1, value ? 0.05 : 0.02, 0, Math.PI * 2); + C.fill(); +}); diff --git a/canvas/triangular-lattice-sampler.js b/canvas/triangular-lattice-sampler.js new file mode 100644 index 0000000..be4e1ab --- /dev/null +++ b/canvas/triangular-lattice-sampler.js @@ -0,0 +1,90 @@ +//Create useful variables +const aspect = W / H; +const scale = aspect >= 1 ? H / 2 : W / 2; + +function setup() { + const [W, H] = [400, 400]; + canvas.width = W; + canvas.height = H; + + // Reset transform + C.setTransform(1, 0, 0, 1, 0, 0); + + // Clear canvas + C.clearRect(0, 0, W, H); + + // Reset common properties + C.globalCompositeOperation = 'source-over'; + C.fillStyle = 'rgba(255,200,100,1)'; + C.strokeStyle = 'rgba(0,0,0,1)'; + + + // Set up transform to map -1,-1..1,1 to canvas with aspect fit + const offsetX = W / 2; + const offsetY = H / 2; + + //Apply transform + C.translate(offsetX, offsetY); + C.scale(scale, -scale); + + // Set 1 pixel wide lines + C.lineWidth = 1 / scale; +} + + +setup(); + + + +const { Bitfield_Image_Sampler } = await import('../lib/image-functions.js'); +//Load +const sampler = Bitfield_Image_Sampler.from_string('16,16,AAAAAAAAAAAAAA4AEQDx/xGgDqAAAAAAAAAAAAAAAAA='); +console.log(sampler); + +const s = 0.2; +for (let y = -10; y < 10; y++) { + const xo = y & 1 ? s * .5 : 0; + const sxo = 0; + //const sxo = y & 1 ? 1 : 0; + for (let x = -10; x < 10; x++) { + const p0 = [xo + (x + .5) * s, y * s]; + const p1 = [xo + (x + 1.5) * s, y * s]; + const p2 = [xo + (x + 1) * s, (y + 1) * s]; + const p3 = [xo + (x + 0) * s, (y + 1) * s]; + + // Filled parallelogram + C.beginPath(); + C.moveTo(...p0); + C.lineTo(...p1); + C.lineTo(...p2); + C.lineTo(...p3); + C.closePath(); + + if (sampler.sample(sxo + x + 8, 8 - y)) { + C.fillStyle = `#cf8`; + } else { + C.fillStyle = `#f88`; + } + + C.fill(); + + // Triangle lines + C.strokeStyle = '#000'; + C.lineWidth = 1 / scale; + + // Outline parallelogram + C.beginPath(); + C.moveTo(...p0); + C.lineTo(...p1); + C.lineTo(...p2); + C.lineTo(...p3); + C.closePath(); + C.stroke(); + + // Diagonal from p0 to p2 + C.beginPath(); + C.moveTo(...p0); + C.lineTo(...p2); + C.stroke(); + } +} diff --git a/index.html b/index.html index 854c56c..8224096 100644 --- a/index.html +++ b/index.html @@ -11,6 +11,8 @@
  • Default configuration
  • Triangular lattice
  • Triangular lattice - corrected offset
  • +
  • Triangular lattice - sampling raster
  • +
  • Image loading
  • diff --git a/lib/image-functions.js b/lib/image-functions.js index b19e1ec..70419bf 100644 --- a/lib/image-functions.js +++ b/lib/image-functions.js @@ -16,6 +16,77 @@ export function load_image_from_src(src) { }); } +export class Bitfield_Image_Sampler { + constructor(width, height, data) { + Object.assign(this, { width, height, data }); + } + + static from_string(encoded) { + const [width_str, height_str, b64] = encoded.split(','); + const width = parseInt(width_str, 10); + const height = parseInt(height_str, 10); + const decoded = atob(b64); + const data = new Uint8Array(decoded.length); + for (let i = 0; i < decoded.length; i++) { + data[i] = decoded.charCodeAt(i); + } + return new this(width, height, data); + } + + *iter_row(row) { + const { width, data } = this; + const row_offset = row * Math.ceil(width / 8); + let byte_index = row_offset; + let bit_mask = 1; + let byte = data[byte_index++] ?? 0; + + for (let x = 0; x < width; x++) { + yield (byte & bit_mask) !== 0; + bit_mask <<= 1; + if (bit_mask === 256) { + bit_mask = 1; + byte = data[byte_index++] ?? 0; + } + } + } + + *[Symbol.iterator]() { + for (let y = 0; y < this.height; y++) { + yield new Image_Row_Reference(this, y); + } + } + + sample(rawx, rawy) { + const { width, height, data } = this; + + const x = Math.floor(rawx); + const y = Math.floor(rawy); + + if (x < 0 || y < 0 || x >= width || y >= height ) { + return undefined; + } + + const row_offset = y * Math.ceil(width / 8); + let byte_index = row_offset + Math.floor(x / 8); + + return data[byte_index] & (1 << x % 8) + } + + pixel_map(cb) { + let y = 0; + for (const row of this) { + let x = 0; + for (const pixel of row) { + cb(x, y, pixel); + x++; + } + y++; + } + } + +} + + export class Image_Row_Reference { constructor(sampler, row) { Object.assign(this, { sampler, row });