๐Rect
an object that has origin (x, y) and size (width, height).
๐ custom
replit โฉ Rect (v1.1) , require -> Vector, Size
// (Rect 1.1)
// 2023.01.22 - 22:57 (+) .points
// (+) .topRight, .topLeft, .top, .bottom, .left, .right
// 2023.01.22 - 20:18 (+) .translate()
// 2023.01.21 - 13:38 (+) .inset()
// 2023.01.18 - 14:33 (+) .toString()
// 2023.01.18 - 09:01 (/) refactor rect()
// 2023.01.17 - 22:19 (โข) first draft
// -------------------------------------------------
const {min, abs} = Math;
// โญ๏ธ import
// -------------------------------------------------
import {vec} from './Vector.js'; // ๐ Vector
import {Size} from './Size.js'; // ๐ Size
// โญ๏ธ Rect
// -------------------------------------------------
// - new Rect(x, y, w, h)
// - rect(x, y, w, h)
// - rect(corner, offset)
// - rect(center, offset, true)
// -------------------------------------------------
// ๐ธ .x, .y, .width, .height
// ๐ธ .coords - [x, y, w, h]
// ๐ธ .size
// -------------------------------------------------
// ๐ธ .origin, .offset - vectors
// ๐ธ .center, .bottomLeft, .bottomRight
// ๐ธ .bottomLeft, .bottomRight, .topLeft, .topRight
// ๐ธ .top
// ๐ธ .points
// -------------------------------------------------
// .halfWidth, .halfHeight, .halfOffset
// -------------------------------------------------
// ๐น .inset()
// ๐น .translate()
// -------------------------------------------------
// ๐น .toString()
//
class Rect {
// init
constructor(
x, y, // a corner point, not necessarily the top-left corner
width, height // a "vector" pointing to the opposite corner
) {
// top-left corner
this.x = min(x, x + width);
this.y = min(y, y + height);
// always positive width/height
this.width = abs(width);
this.height = abs(height);
}
// ------------------------
// size related
// ------------------------
// ๐ธ .size
get size() {
return new Size(this.width, this.height) // ๐ Size
}
// ๐ธ .halfWidth, .halfHeight
get halfWidth() { return this.width / 2 }
get halfHeight() { return this.height / 2 }
// ------------------------
// vectors / points
// ------------------------
// ๐ธ .origin (topLeft)
get origin() { return vec(this.x, this.y) } // ๐ Vector
get topLeft() { return this.origin }
// ๐ธ .origin, .offset, .halfOffset
get offset() { return vec(this.width, this.height) } // ๐ Vector
get halfOffset() { return this.offset.times(1/2) } // ๐ Vector
// ๐ธ .center
get center() { return this.origin.plus(this.halfOffset) }
// ๐ธ .bottomLeft
get bottomLeft() {
const {halfWidth: w, halfHeight: h} = this; // ๐ Vector
return this.center.plus(-w, h);
}
// ๐ธ .bottomRight
get bottomRight() {
return this.origin.plus(this.offset); // ๐ Vector
}
// ๐ธ .topRight
get topRight() {
const {halfWidth: w, halfHeight: h} = this; // ๐ Vector
return this.center.plus(w, -h);
}
// ๐ธ .top
get top() {
return this.center.plus(0, -this.halfHeight);
}
// ๐ธ .bottom
get bottom() {
return this.center.plus(0, this.halfHeight);
}
// ๐ธ .left
get left() {
return this.center.plus(-this.halfWidth, 0);
}
// ๐ธ .right
get right() {
return this.center.plus(this.halfWidth, 0);
}
// ๐ธ .points
get points() {
const {
bottomLeft: p0, bottomRight: p1,
topRight: p2, topLeft: p3,
} = this;
return [p0, p1, p2, p3];
}
// ------------------------
// rect operations
// ------------------------
// ๐น .inset()
inset(dx, dy = dx) {
const v = vec(dx, dy);
// new origin: o + v
const origin = this.origin.plus(v);
// new corner: o + diag - v (diag = this.offset)
// new offset = (o + diag - v) - (o + v) = diag - 2v
const offset = this.offset.plus(v.times(-2));
return rect(origin, offset);
}
// ๐น .translate()
translate(...args) {
const v = vec(...args); // vec(v) or vec(x, y)
return rect(this.origin.plus(v), this.offset);
}
// ------------------------
// helpers / debug
// ------------------------
// rect.coords
get coords() {
const {x, y, width, height} = this;
return [x, y, width, height];
}
// ๐น .toString()
toString() {
return `(${this.coords})`;
}
}
// convenience factory functions
// ๐ธ rect()
function rect(...args) {
const len = args.length;
switch (len) {
// treat as (x, y, width, height)
case 4: return new Rect(...args);
// treat as (point, offset, center?)
case 2:
case 3:
const [point, offset, center] = args;
// `point` is corner
if (!center) return new Rect(...point.coords, ...offset.coords);
// `point` is center
const corner = point.plus(offset);
const offsetToOppositeCorner = offset.times(-2);
return rect(corner, offsetToOppositeCorner);
default:
const msg = [
`โ rect(${args})`,
`โข expecting 2/3/4 arguments, but got ${len}.`,
``,
`๐ก rect() syntax:`,
`------------------`,
`โข rect(x, y, width, height)`,
`โข rect(corner, offset): "corner" and "offset" are vectors.`,
`โข rect(center, offset, true): "true" means "center" is a center point.`,
];
throw new Error(msg.join('\n'));
}
}
// export
export {Rect, rect}; // ES module export
๐พ replit๏ผRect
const { PI } = Math;
const { log } = console;
function deg(x) { return PI * x / 180 } // degrees
// โญ๏ธ import
import {vec, polar} from './js/Vector.js'; // Vector
import {rect} from './js/Rect.js'; // Rect
// ๐ธ ctx.polygon(): regular polygon (subpath)
//
// parameters:
// - n : number of sides
// - x, y: center
// - r : radius
//
// require:
// - Vector, vec(), polar()
CanvasRenderingContext2D.prototype.polygon = function(n, x, y, r, {
startAngle = -Math.PI / 2, // starting angle
clockwise = true, // clockwise by default
} = {}) {
const center = vec(x, y); // center of polygon
const delta = 2 * Math.PI / n * (clockwise ? 1 : -1); // dฮธ = ยฑ 2ฯ / n
// begin new subpath (at first vertex)
this.moveTo(...center.plus(polar(r, startAngle)).coords);
// connect first (n-1) sides
for (let i = 1; i < n; i++) {
this.lineTo(...center.plus(polar(r, startAngle + delta * i)).coords);
}
// connect back to first vertex
this.closePath();
};
// โญ draw on <canvas> 2D context
function drawOnCanvas2D(selector, draw) {
let canvas = document.querySelector(selector);
if (!canvas) { console.log(`โ no such element: "${selector}"`); return; }
let context = canvas.getContext("2d");
if (draw) draw(context);
}
// ------------------
// main
// ------------------
// draw on canvas
drawOnCanvas2D('#playground', (c) => {
// drawing settings
const center = vec(200, 225); // triangle center
const R = 200, r = 80; // radii (large/small)
// text
const text = "<canvas>";
const textHeight = (R - r)/2;
c.font = `bold ${textHeight}pt sans-serif`; // Big font
const textWidth = c.measureText(text).width;
const textCenter = center.plus(0, R / 2 - R / 16);
const textRect = rect(textCenter, vec(textWidth/2, textHeight/2), {center: true});
log(`text size: ${textRect.size}`);
c.strokeRect(...textRect.coords);
c.strokeText(text, ...textRect.bottomLeft.coords);
// vertical stripe down the middle
const stripCenter = center.plus(0, -R/4); // center of triangle height
const stripHalfDimension = vec(40, .85*R);
const strip = rect(stripCenter, stripHalfDimension, {center: true});
c.strokeRect(...strip.coords);
// create path for clipping region
c.polygon(3, ...center.coords, R);
c.polygon(3, ...center.coords, r, { clockwise: false });
// โญ make that path the clipping region
c.clip();
c.fillStyle = 'hsl(0 80% 10% / 0.05)';
c.fill();
c.stroke(); // โญ half of line width will be clipped away
// fill strip (inside the clipping region)
c.fillStyle = "hsl(270 80% 50% / 0.8)";
c.fillRect(...strip.coords);
// fill text (inside the clipping region)
c.fillStyle = "hsl(330 80% 50% / 0.8)";
c.fillText(text, ...textRect.bottomLeft.coords);
});
// โญ๏ธ log
// -------------------------------------------------------
;[
'๐ Have a Good Day!',
].forEach(x => log(x));
History
0: (?) first recorded
1: (+) refactor, .toString()
2: (+) .inset()
// ------------------
// โญ๏ธ Rect
// ------------------
class Rect {
// init
constructor(
x, y, // a corner point, not necessarily the top-left corner
width, height // a "vector" pointing to the opposite corner
) {
// top-left corner
this.x = Math.min(x, x + width);
this.y = Math.min(y, y + height);
// always positive width/height
this.width = Math.abs(width);
this.height = Math.abs(height);
}
// rect.coords
get coords() {
const {x, y, width, height} = this;
return [x, y, width, height];
}
// convenience properties
get halfWidth() { return this.width / 2 }
get halfHeight() { return this.height / 2 }
// key points/vectors
get origin() { return vec(this.x, this.y) }
get offset() { return vec(this.width, this.height) }
get halfOffset() { return this.offset.times(1/2) }
get center() { return this.origin.add(this.halfOffset) }
get bottomLeft() { return this.center.plus(-this.halfWidth, this.halfHeight) }
}
// convenience factory functions
function rect(corner, offset) { return new Rect(...corner.coords, ...offset.coords) }
// ๐ธ Rect.withCenter()
Rect.withCenter = function(
center, // center of rect
offset // offset "vector" to a corner
) {
const corner = center.add(offset);
const offsetToOppositeCorner = offset.times(-2);
return rect(corner, offsetToOppositeCorner);
};
// 2023.01.18 - 14:33 (+) .toString()
// 2023.01.18 - 09:01 (/) refactor rect()
// 2023.01.17 - 22:19 (โข) first draft
// -------------------------------------------------
const {min, abs} = Math;
// โญ๏ธ import
// -------------------------------------------------
import {vec} from './Vector.js'; // ๐ Vector
import {Size} from './Size.js'; // ๐ Size
// โญ๏ธ Rect
// -------------------------------------------------
// - new Rect(x, y, w, h)
// - rect(x, y, w, h)
// - rect(corner, offset)
// - rect(center, offset, true)
// -------------------------------------------------
// .x, .y, .width, .height
// .coords - [x, y, w, h]
// .size
// .origin, .offset
// .center, .bottomLeft
// -------------------------------------------------
// .halfWidth, .halfHeight, .halfOffset
// -------------------------------------------------
// ๐น .toString()
//
class Rect {
// init
constructor(
x, y, // a corner point, not necessarily the top-left corner
width, height // a "vector" pointing to the opposite corner
) {
// top-left corner
this.x = min(x, x + width);
this.y = min(y, y + height);
// always positive width/height
this.width = abs(width);
this.height = abs(height);
}
// rect.coords
get coords() {
const {x, y, width, height} = this;
return [x, y, width, height];
}
// .size
get size() {
return new Size(this.width, this.height) // ๐ Size
}
// .halfWidth, .halfHeight
get halfWidth() { return this.width / 2 }
get halfHeight() { return this.height / 2 }
// .origin, .offset, .halfOffset
get origin() { return vec(this.x, this.y) } // ๐ Vector
get offset() { return vec(this.width, this.height) } // ๐ Vector
get halfOffset() { return this.offset.times(1/2) } // ๐ Vector
// .center, .bottomLeft
get center() { return this.origin.plus(this.halfOffset) } // ๐ Vector
get bottomLeft() {
const {halfWidth: w, halfHeight: h} = this; // ๐ Vector
return this.center.plus(-w, h); // ๐ Vector
}
// ๐น .toString()
toString() {
return `(${this.coords})`;
}
}
// convenience factory functions
// ๐ธ rect()
function rect(...args) {
const len = args.length;
switch (len) {
// treat as (x, y, width, height)
case 4: return new Rect(...args);
// treat as (point, offset, center?)
case 2:
case 3:
const [point, offset, center] = args;
// `point` is corner
if (!center) return new Rect(...point.coords, ...offset.coords);
// `point` is center
const corner = point.plus(offset);
const offsetToOppositeCorner = offset.times(-2);
return rect(corner, offsetToOppositeCorner);
default:
const msg = [
`โ rect(${args})`,
`โข expecting 2/3/4 arguments, but got ${len}.`,
``,
`๐ก rect() syntax:`,
`------------------`,
`โข rect(x, y, width, height)`,
`โข rect(corner, offset): "corner" and "offset" are vectors.`,
`โข rect(center, offset, true): "true" means "center" is a center point.`,
];
throw new Error(msg.join('\n'));
}
}
// export
export {Rect, rect}; // ES module export
replit โฉ various boxes , require -> Vector, Size
// 2023.01.21 - 13:38 (+) .inset()
// 2023.01.18 - 14:33 (+) .toString()
// 2023.01.18 - 09:01 (/) refactor rect()
// 2023.01.17 - 22:19 (โข) first draft
// -------------------------------------------------
const {min, abs} = Math;
// โญ๏ธ import
// -------------------------------------------------
import {vec} from './Vector.js'; // ๐ Vector
import {Size} from './Size.js'; // ๐ Size
// โญ๏ธ Rect
// -------------------------------------------------
// - new Rect(x, y, w, h)
// - rect(x, y, w, h)
// - rect(corner, offset)
// - rect(center, offset, true)
// -------------------------------------------------
// ๐ธ .x, .y, .width, .height
// ๐ธ .coords - [x, y, w, h]
// ๐ธ .size
// ๐ธ .origin, .offset - vectors
// ๐ธ .center, .bottomLeft, .bottomRight
// -------------------------------------------------
// .halfWidth, .halfHeight, .halfOffset
// -------------------------------------------------
// ๐น .inset()
// -------------------------------------------------
// ๐น .toString()
//
class Rect {
// init
constructor(
x, y, // a corner point, not necessarily the top-left corner
width, height // a "vector" pointing to the opposite corner
) {
// top-left corner
this.x = min(x, x + width);
this.y = min(y, y + height);
// always positive width/height
this.width = abs(width);
this.height = abs(height);
}
// ------------------------
// size related
// ------------------------
// ๐ธ .size
get size() {
return new Size(this.width, this.height) // ๐ Size
}
// ๐ธ .halfWidth, .halfHeight
get halfWidth() { return this.width / 2 }
get halfHeight() { return this.height / 2 }
// ------------------------
// vectors / points
// ------------------------
// ๐ธ .origin, .offset, .halfOffset
get origin() { return vec(this.x, this.y) } // ๐ Vector
get offset() { return vec(this.width, this.height) } // ๐ Vector
get halfOffset() { return this.offset.times(1/2) } // ๐ Vector
// ๐ธ .center
get center() { return this.origin.plus(this.halfOffset) } // ๐ Vector
// ๐ธ .bottomLeft
get bottomLeft() {
const {halfWidth: w, halfHeight: h} = this; // ๐ Vector
return this.center.plus(-w, h); // ๐ Vector
}
// ๐ธ .bottomRight
get bottomRight() {
return this.origin.plus(this.offset); // ๐ Vector
}
// ------------------------
// rect
// ------------------------
// ๐น .inset()
inset(dx, dy = dx) {
const v = vec(dx, dy);
// new origin: o + v
const origin = this.origin.plus(v);
// new corner: o + diag - v (diag = this.offset)
// new offset = (o + diag - v) - (o + v) = diag - 2v
const offset = this.offset.plus(v.times(-2));
return rect(origin, offset);
}
// ------------------------
// helpers / debug
// ------------------------
// rect.coords
get coords() {
const {x, y, width, height} = this;
return [x, y, width, height];
}
// ๐น .toString()
toString() {
return `(${this.coords})`;
}
}
// convenience factory functions
// ๐ธ rect()
function rect(...args) {
const len = args.length;
switch (len) {
// treat as (x, y, width, height)
case 4: return new Rect(...args);
// treat as (point, offset, center?)
case 2:
case 3:
const [point, offset, center] = args;
// `point` is corner
if (!center) return new Rect(...point.coords, ...offset.coords);
// `point` is center
const corner = point.plus(offset);
const offsetToOppositeCorner = offset.times(-2);
return rect(corner, offsetToOppositeCorner);
default:
const msg = [
`โ rect(${args})`,
`โข expecting 2/3/4 arguments, but got ${len}.`,
``,
`๐ก rect() syntax:`,
`------------------`,
`โข rect(x, y, width, height)`,
`โข rect(corner, offset): "corner" and "offset" are vectors.`,
`โข rect(center, offset, true): "true" means "center" is a center point.`,
];
throw new Error(msg.join('\n'));
}
}
// export
export {Rect, rect}; // ES module export
Last updated