Last active
April 11, 2021 12:40
-
-
Save tomowarkar/ad80230715e8779920f9b16bd92eba53 to your computer and use it in GitHub Desktop.
https://editor.p5js.org/ コピペで動かせるぞ
This file contains hidden or 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
class RotMat2 { | |
constructor(angle) { | |
this.cosa = Math.cos(angle) | |
this.sina = Math.sin(angle) | |
} | |
apply(v) { | |
return new Vec2( | |
this.cosa * v.x - this.sina * v.y, | |
this.sina * v.x + this.cosa * v.y | |
) | |
} | |
} | |
class Vec2 { | |
constructor(x, y) { | |
this.x = x === undefined ? 0.0: x | |
this.y = y === undefined ? this.x: y | |
} | |
static _exec(left, right, operator) { | |
right = Number.isFinite(right) ? new Vec2(right) : right | |
switch (operator) { | |
case '+': | |
return new Vec2(left.x + right.x, left.y + right.y) | |
case '-': | |
return new Vec2(left.x - right.x, left.y - right.y) | |
case '*': | |
return new Vec2(left.x * right.x, left.y * right.y) | |
case '/': | |
return new Vec2(left.x / right.x, left.y / right.y) | |
case '==': | |
case '===': | |
return left.x === right.x && left.y === right.y | |
case 'dot': | |
return left.x * right.x + left.y * right.y | |
case 'det': | |
return left.x * right.y - left.y * right.x | |
case 'distance': | |
return Vec2._exec(left, right, "-").length | |
case 'angle': | |
const det = Vec2._exec(left, right, "det"), | |
dot = Vec2._exec(left, right, "dot") | |
return Math.atan2(det, dot); | |
default: | |
return left | |
} | |
} | |
apply(v) { | |
this.x = v.x | |
this.y = v.y | |
} | |
add(other) { | |
return Vec2._exec(this, other, "+") | |
} | |
addeq(other) { | |
this.apply(this.add(other)) | |
} | |
sub(other) { | |
return Vec2._exec(this, other, "-") | |
} | |
subeq(other) { | |
this.apply(this.sub(other)) | |
} | |
mul(other) { | |
return Vec2._exec(this, other, "*") | |
} | |
muleq(other) { | |
this.apply(this.mul(other)) | |
} | |
div(other) { | |
return Vec2._exec(this, other, "/") | |
} | |
diveq(other) { | |
this.apply(this.div(other)) | |
} | |
isEqual(other) { | |
return Vec2._exec(this, other, "==") | |
} | |
get length() { | |
// return Math.sqrt(this.x * this.x + this.y * this.y) | |
return Math.hypot(this.x, this.y) | |
} | |
get normal() { | |
return new Vec2(-this.y, this.x) | |
} | |
normalized() { | |
const length = this.length | |
return new Vec2(this.x / length, this.y / length) | |
} | |
rotated(angle, origin) { | |
const v = this.sub(origin), | |
m = new RotMat2(angle) | |
return m.apply(v).add(origin) | |
} | |
} | |
class Particule { | |
constructor(pos, m) { | |
pos = pos === undefined ? new Vec2() : pos | |
m = m === undefined ? 1.0 : m | |
this.position = pos | |
this.old_position = pos | |
this.acceleration = new Vec2(0.0, 0.0) | |
this.mass = m | |
} | |
get inv_mass() { | |
return 1/this.mass | |
} | |
update(dt, air_friction) { | |
air_friction = air_friction === undefined ? 0.5 : air_friction | |
const velocity = this.position.sub(this.old_position) | |
this.acceleration.subeq(velocity.mul(air_friction)) | |
const new_pos = this.position.add(velocity.add(this.acceleration.mul(dt))) | |
this.old_position = this.position | |
this.position = new_pos | |
this.acceleration = new Vec2(0.0, 0.0) | |
} | |
applyForce(v) { | |
this.acceleration.addeq(v.mul(1-this.inv_mass)) | |
} | |
move(v) { | |
this.position.addeq(add) | |
} | |
} | |
class pParticule extends Particule { | |
constructor(...params) { | |
super(...params) | |
} | |
get radius() { | |
return Math.cbrt(this.mass * 3 / 4 / Math.PI) * 30 | |
} | |
draw() { | |
ellipse( | |
(this.position.x%width+width)%width, | |
(this.position.y%height+height)%height, | |
this.radius | |
); | |
} | |
} | |
let particules = [] | |
const NUM = 50 | |
function setup() { | |
createCanvas(400, 400); | |
for (let i = 0; i< NUM; i++) { | |
let pos = new Vec2(random(0, width), random(0, height)) | |
particules.push(new pParticule(pos, random(2, 7))) | |
} | |
// noLoop() | |
} | |
function draw() { | |
background(220); | |
particules.sort((a, b)=>{ | |
return b.mass - a.mass | |
}) | |
for (let i = 0; i < particules.length; i++) { | |
let p = particules[i] | |
p.applyForce(new Vec2(random(-1, 1), random(-1, 1))) | |
p.applyForce(new Vec2(0, -5)) | |
p.update(1) | |
p.draw() | |
} | |
} |
This file contains hidden or 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
class RotMat2 { | |
constructor(angle) { | |
this.cosa = Math.cos(angle) | |
this.sina = Math.sin(angle) | |
} | |
apply(v) { | |
return new Vec2( | |
this.cosa * v.x - this.sina * v.y, | |
this.sina * v.x + this.cosa * v.y | |
) | |
} | |
} | |
class MinMat2x2 { | |
constructor(...values) { | |
const flag = values.length === 4 | |
this.a = flag ? values[0] : 1 | |
this.b = flag ? values[1] : 0 | |
this.c = flag ? values[2] : 0 | |
this.d = flag ? values[3] : 1 | |
} | |
det() { | |
return this.a * this.d - this.b * this.c | |
} | |
adj() { | |
return new MinMat2x2(this.d, -this.b, -this.c, this.a) | |
} | |
inv() { | |
const det = this.det() | |
if (det === 0) throw new Error("zerodiv") | |
return new MinMat2x2(...Object.values(this.adj()).map(e=>e/det)) | |
} | |
dot(other) { | |
if (other.hasOwnProperty("x") && other.hasOwnProperty("y")) { | |
return new Vec2( | |
this.a * other.x + this.b * other.y, | |
this.c * other.x + this.d * other.y | |
) | |
} | |
return new MinMat2x2( | |
this.a * other.a + this.b * other.c, | |
this.a * other.b + this.b * other.d, | |
this.c * other.a + this.d * other.c, | |
this.c * other.b + this.d * other.d | |
) | |
} | |
} | |
class Vec2 { | |
constructor(x, y) { | |
this.x = x === undefined ? 0.0: x | |
this.y = y === undefined ? this.x: y | |
} | |
static _exec(left, right, operator) { | |
right = Number.isFinite(right) ? new Vec2(right) : right | |
switch (operator) { | |
case '+': | |
return new Vec2(left.x + right.x, left.y + right.y) | |
case '-': | |
return new Vec2(left.x - right.x, left.y - right.y) | |
case '*': | |
return new Vec2(left.x * right.x, left.y * right.y) | |
case '/': | |
return new Vec2(left.x / right.x, left.y / right.y) | |
case '==': | |
case '===': | |
return left.x === right.x && left.y === right.y | |
case 'dot': | |
return left.x * right.x + left.y * right.y | |
case 'det': | |
return left.x * right.y - left.y * right.x | |
case 'distance': | |
return Vec2._exec(left, right, "-").length | |
case 'angle': | |
const det = Vec2._exec(left, right, "det"), | |
dot = Vec2._exec(left, right, "dot") | |
return Math.atan2(det, dot); | |
default: | |
return left | |
} | |
} | |
apply(v) { | |
this.x = v.x | |
this.y = v.y | |
} | |
add(other) { | |
return Vec2._exec(this, other, "+") | |
} | |
addeq(other) { | |
this.apply(this.add(other)) | |
} | |
sub(other) { | |
return Vec2._exec(this, other, "-") | |
} | |
subeq(other) { | |
this.apply(this.sub(other)) | |
} | |
mul(other) { | |
return Vec2._exec(this, other, "*") | |
} | |
muleq(other) { | |
this.apply(this.mul(other)) | |
} | |
div(other) { | |
return Vec2._exec(this, other, "/") | |
} | |
diveq(other) { | |
this.apply(this.div(other)) | |
} | |
isEqual(other) { | |
return Vec2._exec(this, other, "==") | |
} | |
get length() { | |
// return Math.sqrt(this.x * this.x + this.y * this.y) | |
return Math.hypot(this.x, this.y) | |
} | |
get normal() { | |
return new Vec2(-this.y, this.x) | |
} | |
normalized() { | |
const length = this.length | |
return new Vec2(this.x / length, this.y / length) | |
} | |
rotated(angle, origin) { | |
const v = this.sub(origin), | |
m = new RotMat2(angle) | |
return m.apply(v).add(origin) | |
} | |
} | |
// round = (value, base) => Math.round(value * (10**base)) / (10**base) | |
class Vec2x2 { | |
constructor(x1, y1, x2, y2) { | |
this.v1 = new Vec2(x1, y1) | |
this.v2 = new Vec2(x2, y2) | |
} | |
static _intersect(l1, l2) { | |
const p12 = l1.v1.sub(l1.v2), | |
p43 = l2.v2.sub(l2.v1), | |
p42 = l2.v2.sub(l1.v2) | |
let flag = false, | |
m = new Vec2() | |
try { | |
m = new MinMat2x2(p12.x, p43.x, p12.y, p43.y).inv().dot(p42) | |
flag = (0 <= m.x && m.x <= 1 && 0 <= m.y && m.y <= 1) | |
} catch(err) { | |
} | |
return [flag, m.x, m.y] | |
} | |
static intersection(l1, l2) { | |
const [ok, t, s] = Vec2x2._intersect(l1, l2) | |
return [ok, l1.v1.mul(t).add(l1.v2.mul(1-t))] | |
} | |
static drawIntersection(l1, l2) { | |
const [ok, p] = Vec2x2.intersection(l1, l2) | |
if (ok) ellipse(p.x, p.y, 5) | |
} | |
draw(c) { | |
stroke(c) | |
line(this.v1.x, this.v1.y, this.v2.x, this.v2.y) | |
stroke(0) | |
// text(`(${Object.values(this.v1)})`, ...Object.values(this.v1)) | |
// text(`(${Object.values(this.v2)})`, ...Object.values(this.v2)) | |
} | |
} | |
let lines = [ | |
new Vec2x2(50, 200, 350, 200), | |
new Vec2x2(200, 200, 350, 400), | |
] | |
const NUM = 20 | |
function setup() { | |
createCanvas(400, 400); | |
for (let i = 0; i< NUM; i++) { | |
lines.push(new Vec2x2(random(0, width), random(0, height), random(0, width), random(0, height))) | |
} | |
noLoop(); | |
} | |
function draw() { | |
background(220); | |
for (let i = 0; i < lines.length; i++) { | |
lines[i].draw(0) | |
} | |
for (let i = 0; i < lines.length; i++) { | |
for (let j = i+1; j < lines.length; j++) { | |
Vec2x2.drawIntersection(lines[i], lines[j]) | |
} | |
} | |
} |
This file contains hidden or 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
import p5 from "p5"; | |
class Vec2 { | |
x: number; | |
y: number; | |
constructor(x: number, y: number) { | |
this.x = x; | |
this.y = y; | |
} | |
get length() { | |
return Math.hypot(this.x, this.y); | |
} | |
get normal() { | |
return new Vec2(-this.y, this.x); | |
} | |
} | |
class Segment { | |
p1: Vec2; | |
p2: Vec2; | |
constructor(p1: Vec2, p2: Vec2) { | |
this.p1 = p1; | |
this.p2 = p2; | |
} | |
get x() { | |
return this.p2.x - this.p1.x; | |
} | |
get y() { | |
return this.p2.y - this.p1.y; | |
} | |
} | |
class Intersection { | |
s: number; | |
t: number; | |
point: Vec2; | |
isCross: boolean; | |
constructor(p1: Vec2, p2: Vec2, p3: Vec2, p4: Vec2) { | |
this.isCross = false; | |
let s1 = new Segment(p1, p2), | |
s2 = new Segment(p3, p4); | |
this.s = | |
(-s1.y * (p1.x - p3.x) + s1.x * (p1.y - p3.y)) / | |
(-s2.x * s1.y + s1.x * s2.y); | |
this.t = | |
(s2.x * (p1.y - p3.y) - s2.y * (p1.x - p3.x)) / | |
(-s2.x * s1.y + s1.x * s2.y); | |
this.point = new Vec2(p1.x + this.t * s1.x, p1.y + this.t * s1.y); | |
if (this.s >= 0 && this.s <= 1 && this.t >= 0 && this.t <= 1) { | |
this.isCross = true; | |
} | |
} | |
} | |
const sketch = (p: p5) => { | |
const drawSegment = (p1: Vec2, p2: Vec2, c: any) => { | |
p.stroke(c); | |
p.line(p1.x, p1.y, p2.x, p2.y); | |
p.stroke(0); | |
}; | |
const drawPoint = (v: Vec2, c: any) => { | |
p.ellipse(v.x, v.y, c); | |
}; | |
const drawIntersection = (p1: Vec2, p2: Vec2, p3: Vec2, p4: Vec2) => { | |
let isec = new Intersection(p1, p2, p3, p4); | |
if (isec.isCross) { | |
drawPoint(isec.point, 5); | |
} | |
}; | |
let segments: Segment[] = [], | |
num_segments = 10; | |
p.setup = () => { | |
p.createCanvas(p.windowWidth, p.windowHeight); | |
for (let i = 0; i < num_segments; i++) { | |
segments.push( | |
new Segment( | |
new Vec2(p.random(0, p.width), p.random(0, p.height)), | |
new Vec2(p.random(0, p.width), p.random(0, p.height)) | |
) | |
); | |
} | |
p.noLoop(); | |
}; | |
p.draw = () => { | |
p.background(220); | |
for (let i = 0; i < segments.length; i++) { | |
let s = segments[i]; | |
drawSegment(s.p1, s.p2, 0); | |
} | |
for (let i = 0; i < segments.length; i++) { | |
for (let j = i + 1; j < segments.length; j++) { | |
let s1 = segments[i], | |
s2 = segments[j]; | |
drawIntersection(s1.p1, s1.p2, s2.p1, s2.p2); | |
} | |
} | |
}; | |
}; | |
export default sketch; |
This file contains hidden or 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
class Vec2 { | |
constructor(x, y) { | |
if (typeof x === "number" && typeof y === "number") { | |
this.x = x; | |
this.y = y; | |
} else if (typeof x === "object") { | |
this.x = x.x; | |
this.y = x.y; | |
} else { | |
this.x = 0; | |
this.y = 0; | |
} | |
} | |
get len2() { | |
return this.x * this.x + this.y * this.y; | |
} | |
get len() { | |
return Math.sqrt(this.len2); | |
} | |
get gradients() { | |
return this.y === 0 ? 0 : this.x / this.y; | |
} | |
add(v) { | |
if (typeof v === "number") v = new Vec2(v, v); | |
return new Vec2(this.x + v.x, this.y + v.y); | |
} | |
sub(v) { | |
if (typeof v === "number") v = new Vec2(v, v); | |
return new Vec2(this.x - v.x, this.y - v.y); | |
} | |
mul(v) { | |
if (typeof v === "number") v = new Vec2(v, v); | |
return new Vec2(this.x * v.x, this.y * v.y); | |
} | |
div(v) { | |
if (typeof v === "number") v = new Vec2(v, v); | |
return new Vec2(this.x / v.x, this.y / v.y); | |
} | |
_draw(d) { | |
circle(this.x, height - this.y, d || 10); | |
} | |
draw(d) { | |
this._draw(d); | |
} | |
} | |
class Segment { | |
constructor(p1, p2) { | |
this.p1 = p1; | |
this.p2 = p2; | |
} | |
static make(x1, y1, x2, y2) { | |
return new Segment(new Vec2(x1, y1), new Vec2(x2, y2)); | |
} | |
get p() { | |
return this.p2.sub(this.p1); | |
} | |
get x() { | |
return this.p.x; | |
} | |
get y() { | |
return this.p.y; | |
} | |
get x1() { | |
return this.p1.x; | |
} | |
get y1() { | |
return this.p1.y; | |
} | |
get x2() { | |
return this.p2.x; | |
} | |
get y2() { | |
return this.p2.y; | |
} | |
get len2() { | |
return this.p.len2; | |
} | |
get len() { | |
return this.p.len; | |
} | |
get center() { | |
return this.p1.add(this.p2).div(2); | |
} | |
// ax + by + c = 0 | |
get a() { | |
let p = this.p; | |
if (p.x === 0) return 0; | |
return p.y / p.x; | |
} | |
get b() { | |
return -1; | |
} | |
get c() { | |
return this.y1 - this.a * this.x1; | |
} | |
toCircle() { | |
return new Circle(this.center, this.len / 2); | |
} | |
d2(x, y) { | |
let a = this.a, | |
b = this.b, | |
d = a * x + b * y + this.c; | |
return (d * d) / (a * a + b * b); | |
} | |
draw() { | |
line(this.x1, height - this.y1, this.x2, height - this.y2); | |
} | |
} | |
class Rect { | |
constructor(leftbottom, righttop) { | |
this.p1 = leftbottom; | |
this.p3 = righttop; | |
this.p2 = new Vec2(leftbottom.x, righttop.y) | |
this.p4 = new Vec2(righttop.x, leftbottom.y) | |
} | |
static make(x, y, w, h) { | |
return new Rect(new Vec2(x, y), new Vec2(x+w, y+h)) | |
} | |
get x1(){ | |
return this.p1.x | |
} | |
get x2(){ | |
return this.p3.x | |
} | |
get y1(){ | |
return this.p1.y | |
} | |
get y2(){ | |
return this.p3.y | |
} | |
get bottomSeg() { | |
return new Segment(this.p1, this.p4) | |
} | |
get topSeg() { | |
return new Segment(this.p2, this.p3) | |
} | |
get leftSeg() { | |
return new Segment(this.p1, this.p2) | |
} | |
get rightSeg() { | |
return new Segment(this.p3, this.p4) | |
} | |
draw() { | |
this.bottomSeg.draw(); | |
this.topSeg.draw(); | |
this.leftSeg.draw(); | |
this.rightSeg.draw(); | |
} | |
} | |
class Circle extends Vec2 { | |
constructor(x, y, r) { | |
if (typeof x === "object") { | |
super(x); | |
this.r = y; | |
} else { | |
super(x, y); | |
this.r = r; | |
} | |
this.r = this.r || 30; | |
} | |
draw() { | |
this._draw(this.r * 2); | |
} | |
} | |
class Intersection { | |
constructor(s1, s2) { | |
this.isCross = false; | |
let p1 = s1.p1, | |
p3 = s2.p1; | |
this.s = | |
(-s1.y * (p1.x - p3.x) + s1.x * (p1.y - p3.y)) / | |
(-s2.x * s1.y + s1.x * s2.y); | |
this.t = | |
(s2.x * (p1.y - p3.y) - s2.y * (p1.x - p3.x)) / | |
(-s2.x * s1.y + s1.x * s2.y); | |
this.point = new Vec2(p1.x + this.t * s1.x, p1.y + this.t * s1.y); | |
if (this.s >= 0 && this.s <= 1 && this.t >= 0 && this.t <= 1) { | |
this.isCross = true; | |
} | |
this.s1 = s1; | |
this.s2 = s2; | |
} | |
static make(p1, p2, p3, p4) { | |
return new Intersection(new Segment(p1, p2), new Segment(p3, p4)); | |
} | |
static _(a1, b1, c1, a2, b2, c2) { | |
let d = a1 * b2 - a2 * b1; | |
if (d === 0) return; | |
return new Vec2((b1 * c2 - b2 * c1) / d, (a2 * c1 - a1 * c2) / d); | |
} | |
static foot(x, y, a, b, c) { | |
return Intersection._(a, b, c, b, -a, a * y - b + x); | |
} | |
draw() { | |
if (this.point) this.point.draw(10); | |
} | |
} | |
const CollisionCircles = (b1, b2) => { | |
let s = new Segment(b1, b2); | |
return s.len2 <= (b1.r + b2.r) * (b1.r + b2.r); | |
}; | |
const CollisionCircleSeg = (circle, seg) => { | |
if (seg.d2(circle.x, circle.y) > circle.r * circle.r) { | |
return false; | |
} | |
return CollisionCircles(circle, seg.toCircle()); | |
}; | |
class A extends Circle { | |
constructor(x, y, r) { | |
super(x, y, r) | |
} | |
fix(rect) { | |
if (this.x < rect.x1+this.r) this.x = rect.x1+this.r | |
if (this.y < rect.y1+this.r) this.y = rect.y1+this.r | |
if (rect.x2-this.r < this.x ) this.x = rect.x2-this.r | |
if (rect.y2-this.r < this.y ) this.y = rect.y2-this.r | |
if (this.x < rect.x1+this.r && this.x < rect.x1+this.r){ | |
this.x = (rect.x1 + rect.x2)/2 | |
} | |
} | |
} | |
let bigRect, pointer, seg, smallRect; | |
let x, y; | |
function setup() { | |
createCanvas(400, 400); | |
bigRect = new Rect(new Vec2(10, 10), new Vec2(390, 390)); | |
pointer = new A(width / 2, height / 2); | |
seg = Segment.make(100, 200, 300, 300); | |
smallRect = Rect.make(30, 30, 50, 100) | |
// noLoop() | |
} | |
function draw() { | |
background(220); | |
seg.draw(); | |
bigRect.draw(); | |
smallRect.draw() | |
pointer.x = mouseX; | |
pointer.y = height - mouseY; | |
pointer.fix(bigRect) | |
if (CollisionCircleSeg(pointer, seg)) fill("red"); | |
// pointer.fix(r) | |
pointer.draw(); | |
fill(255); | |
} |
This file contains hidden or 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
class Vec2 { | |
constructor(x, y) { | |
this.x = x || 0; | |
this.y = y || 0; | |
} | |
get len2() { | |
return this.x * this.x + this.y * this.y; | |
} | |
get len() { | |
return Math.sqrt(this.len2); | |
} | |
// get gradients() { | |
// return this.x === 0 ? 0 : this.y / this.x; | |
// } | |
get theta() { | |
return Math.atan2(this.y, this.x); | |
} | |
add(v) { | |
if (typeof v === "number") v = new Vec2(v, v); | |
return new Vec2(this.x + v.x, this.y + v.y); | |
} | |
sub(v) { | |
if (typeof v === "number") v = new Vec2(v, v); | |
return new Vec2(this.x - v.x, this.y - v.y); | |
} | |
mul(v) { | |
if (typeof v === "number") v = new Vec2(v, v); | |
return new Vec2(this.x * v.x, this.y * v.y); | |
} | |
div(v) { | |
if (typeof v === "number") v = new Vec2(v, v); | |
return new Vec2(this.x / v.x, this.y / v.y); | |
} | |
move(v) { | |
this.x += v.x; | |
this.y += v.y; | |
} | |
moveTo(v) { | |
this.x = v.x; | |
this.y = v.y; | |
} | |
draw() { | |
circle(this.x, height - this.y, 1); | |
} | |
} | |
const drawline = (v1, v2) => { | |
line(v1.x, height - v1.y, v2.x, height - v2.y); | |
}; | |
class Shape extends Vec2 { | |
constructor(x, y, w, h) { | |
super(x, y); | |
this.w = w || 0; | |
this.h = h || 0; | |
} | |
get p1() { | |
return new Vec2(this.x - this.w / 2, this.y - this.h / 2); | |
} | |
set p1(v) { | |
this.move(v.sub(this.p1)); | |
} | |
get p2() { | |
return new Vec2(this.x - this.w / 2, this.y + this.h / 2); | |
} | |
get p3() { | |
return new Vec2(this.x + this.w / 2, this.y + this.h / 2); | |
} | |
get p4() { | |
return new Vec2(this.x + this.w / 2, this.y - this.h / 2); | |
} | |
draw() { | |
this.frame(); | |
} | |
frame() { | |
drawline(this.p1, this.p2); | |
drawline(this.p2, this.p3); | |
drawline(this.p3, this.p4); | |
drawline(this.p4, this.p1); | |
} | |
_hover(v) { | |
return ( | |
this.p1.x <= v.x && | |
v.x <= this.p3.x && | |
this.p1.y <= v.y && | |
v.y <= this.p3.y | |
); | |
} | |
hover(v) { | |
return this._hover(v); | |
} | |
} | |
class Ellipse extends Shape { | |
draw() { | |
ellipse(this.x, height - this.y, this.w, this.h); | |
} | |
hover(v) { | |
if (!this._hover(v)) { | |
return false; | |
} | |
let a2 = this.w/2 * this.w/2 | |
let b2 = this.h/2 * this.h/2 | |
let v2 = this.sub(v) | |
return (v2.x*v2.x/a2)+ (v2.y*v2.y/b2) <= 1 | |
} | |
} | |
class Circle extends Ellipse { | |
constructor(x, y, r) { | |
super(x, y, 2 * r, 2 * r); | |
} | |
get r() { | |
return this.w / 2; | |
} | |
draw() { | |
circle(this.x, height - this.y, this.w); | |
} | |
} | |
class Fan extends Ellipse { | |
constructor(x, y, w, h, start, end) { | |
super(x, y, w, h); | |
this.start = start; | |
this.end = end; | |
} | |
draw() { | |
arc( | |
this.x, | |
height - this.y, | |
this.w, | |
this.h, | |
TWO_PI - this.end, | |
TWO_PI - this.start | |
); | |
} | |
} | |
class Rect extends Shape { | |
constructor(leftbottom, righttop) { | |
let c = righttop.add(leftbottom).div(2), | |
d = righttop.sub(leftbottom); | |
super(c.x, c.y, d.x, d.y); | |
} | |
} | |
let p, b; | |
function setup() { | |
createCanvas(400, 400); | |
p = new Circle(200, 200, 10); | |
b = new Shape(width / 2, height / 2, 380, 380); | |
// noLoop() | |
} | |
function draw() { | |
background(220); | |
b.frame(); | |
p.moveTo(new Vec2(mouseX, height - mouseY)); | |
// c = new Ellipse(width / 2, height / 2, 100, 200); | |
c = new Circle(100, 200, 100) | |
// c = new Fan(100, 200, 100, 200, -HALF_PI/2, PI) | |
c.draw(); | |
c.frame(); | |
drawline(c, p); | |
if (c.hover(p)) fill("red"); | |
p.draw(); | |
fill(220); | |
} |
This file contains hidden or 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
class Vec2 { | |
constructor(x, y) { | |
this.x = x || 0; | |
this.y = y || 0; | |
} | |
get len2() { | |
return this.x * this.x + this.y * this.y; | |
} | |
get len() { | |
return Math.sqrt(this.len2); | |
} | |
set len(l) { | |
let n = this.normalized().mul(l); | |
this.moveTo(n); | |
} | |
normal(){} | |
normalized() { | |
let len = this.len; | |
return new Vec2(this.x / len, this.y / len); | |
} | |
// get gradients() { | |
// return this.x === 0 ? 0 : this.y / this.x; | |
// } | |
get theta() { | |
return Math.atan2(this.y, this.x); | |
} | |
add(v) { | |
if (typeof v === "number") v = new Vec2(v, v); | |
return new Vec2(this.x + v.x, this.y + v.y); | |
} | |
sub(v) { | |
if (typeof v === "number") v = new Vec2(v, v); | |
return new Vec2(this.x - v.x, this.y - v.y); | |
} | |
mul(v) { | |
if (typeof v === "number") v = new Vec2(v, v); | |
return new Vec2(this.x * v.x, this.y * v.y); | |
} | |
div(v) { | |
if (typeof v === "number") v = new Vec2(v, v); | |
return new Vec2(this.x / v.x, this.y / v.y); | |
} | |
move(v) { | |
this.x += v.x; | |
this.y += v.y; | |
} | |
moveTo(v) { | |
this.x = v.x; | |
this.y = v.y; | |
} | |
draw(s) { | |
circle(this.x, height - this.y, s || 10); | |
} | |
} | |
const drawline = (v1, v2) => { | |
line(v1.x, height - v1.y, v2.x, height - v2.y); | |
}; | |
class Shape extends Vec2 { | |
constructor(x, y, w, h) { | |
super(x, y); | |
this.w = w || 0; | |
this.h = h || 0; | |
} | |
get p1() { | |
return new Vec2(this.x - this.w / 2, this.y - this.h / 2); | |
} | |
set p1(v) { | |
this.move(v.sub(this.p1)); | |
} | |
get p2() { | |
return new Vec2(this.x - this.w / 2, this.y + this.h / 2); | |
} | |
get p3() { | |
return new Vec2(this.x + this.w / 2, this.y + this.h / 2); | |
} | |
get p4() { | |
return new Vec2(this.x + this.w / 2, this.y - this.h / 2); | |
} | |
draw() { | |
this.frame(); | |
} | |
frame() { | |
drawline(this.p1, this.p2); | |
drawline(this.p2, this.p3); | |
drawline(this.p3, this.p4); | |
drawline(this.p4, this.p1); | |
} | |
_hover(v) { | |
return ( | |
this.p1.x <= v.x && | |
v.x <= this.p3.x && | |
this.p1.y <= v.y && | |
v.y <= this.p3.y | |
); | |
} | |
hover(v) { | |
return this._hover(v); | |
} | |
forceInside(v, c1, c2) { | |
if (this.p1.x > v.x) { | |
v.x = this.p1.x; | |
if (c1) c1() | |
} | |
if (v.x > this.p3.x) { | |
v.x = this.p3.x; | |
if (c1) c1() | |
} | |
if (this.p1.y > v.y) { | |
v.y = this.p1.y; | |
if (c2) c2() | |
} | |
if (v.y > this.p3.y) { | |
v.y = this.p3.y; | |
if (c2) c2() | |
} | |
} | |
} | |
class Segment extends Shape { | |
constructor(x1, y1, x2, y2) { | |
super((x1 + x2) / 2, (y1 + y2) / 2, x2 - x1, y2 - y1); | |
} | |
get x1() { | |
return this.p1.x; | |
} | |
get y1() { | |
return this.p1.y; | |
} | |
get x2() { | |
return this.p3.x; | |
} | |
get y2() { | |
return this.p3.y; | |
} | |
get len() { | |
return Math.hypot(this.w, this.h); | |
} | |
get a() { | |
if (this.w === 0) return 0; | |
return this.h / this.x; | |
} | |
get b() { | |
return -1; | |
} | |
get c() { | |
return this.y1 - this.a * this.x1; | |
} | |
d2(v) { | |
// vとの最短距離の乗数 | |
let a = this.a, | |
b = this.b, | |
d = a * v.x + b * v.y + this.c; | |
return (d * d) / (a * a + b * b); | |
} | |
draw() { | |
drawline(this.p1, this.p3); | |
} | |
} | |
class Intersection { | |
constructor(s1, s2) { | |
this.isCross = false; | |
this.s = | |
(-s1.h * (s1.x - s2.x) + s1.w * (s1.y - s2.y)) / | |
(-s2.w * s1.h + s1.w * s2.h); | |
this.t = | |
(s2.w * (s1.y - s2.y) - s2.h * (s1.x - s2.x)) / | |
(-s2.w * s1.h + s1.w * s2.h); | |
this.point = new Vec2(s1.x + this.t * s1.w, s1.y + this.t * s1.h); | |
if (this.s >= 0 && this.s <= 1 && this.t >= 0 && this.t <= 1) { | |
this.isCross = true; | |
} | |
this.s1 = s1; | |
this.s2 = s2; | |
} | |
static _(a1, b1, c1, a2, b2, c2) { | |
let d = a1 * b2 - a2 * b1; | |
if (d === 0) return; | |
return new Vec2((b1 * c2 - b2 * c1) / d, (a2 * c1 - a1 * c2) / d); | |
} | |
static foot(x, y, a, b, c) { | |
return Intersection._(a, b, c, b, -a, a * y - b + x); | |
} | |
draw() { | |
if (this.point) this.point.draw(10); | |
} | |
} | |
class Ellipse extends Shape { | |
draw() { | |
ellipse(this.x, height - this.y, this.w, this.h); | |
} | |
hover(v) { | |
if (!this._hover(v)) { | |
return false; | |
} | |
let a2 = ((this.w / 2) * this.w) / 2; | |
let b2 = ((this.h / 2) * this.h) / 2; | |
let v2 = this.sub(v); | |
return (v2.x * v2.x) / a2 + (v2.y * v2.y) / b2 <= 1; | |
} | |
} | |
class Circle extends Ellipse { | |
constructor(x, y, r) { | |
super(x, y, 2 * r, 2 * r); | |
} | |
get r() { | |
return this.w / 2; | |
} | |
draw() { | |
circle(this.x, height - this.y, this.w); | |
} | |
} | |
class Fan extends Ellipse { | |
constructor(x, y, w, h, start, end) { | |
super(x, y, w, h); | |
this.start = start; | |
this.end = end; | |
} | |
draw() { | |
arc( | |
this.x, | |
height - this.y, | |
this.w, | |
this.h, | |
TWO_PI - this.end, | |
TWO_PI - this.start | |
); | |
} | |
} | |
class Rect extends Shape { | |
constructor(leftbottom, righttop) { | |
let c = righttop.add(leftbottom).div(2), | |
d = righttop.sub(leftbottom); | |
super(c.x, c.y, d.x, d.y); | |
} | |
} | |
class Particle extends Circle { | |
constructor(x, y, r) { | |
super(x, y, r); | |
this.mass = 1; | |
this.inv_mass = 1; | |
this.velocity = new Vec2(0, 0); | |
this.acceleration = new Vec2(0, 0); | |
} | |
get mass() { | |
return this._mass; | |
} | |
set mass(m) { | |
this._mass = m === 0 ? 0.0001 : m; | |
this.inv_mass = 1 / this._mass; | |
} | |
update(dt) { | |
this.velocity = this.velocity.add(this.acceleration.mul(dt)); | |
this.move(this.velocity.mul(dt)); | |
this.acceleration = new Vec2(0, 0); | |
} | |
} | |
let p, b, c; | |
let ball; | |
function setup() { | |
createCanvas(400, 400); | |
p = new Circle(200, 200, 10); | |
b = new Shape(width / 2, height / 2, 380, 380); | |
c = new Circle(200, 200, 100); | |
ball = new Particle(100, 100, 10); | |
// noLoop() | |
} | |
const ballUpdater = (b) => { | |
if (keyIsDown(LEFT_ARROW)) { | |
b.acceleration.x -= 100; | |
} | |
if (keyIsDown(RIGHT_ARROW)) { | |
b.acceleration.x += 100; | |
} | |
if (keyIsDown(UP_ARROW)) { | |
b.acceleration.y += 100; | |
} | |
if (keyIsDown(DOWN_ARROW)) { | |
b.acceleration.y -= 100; | |
} | |
// let v = b.sub(c) | |
// if (v.len <= c.r + b.r) { | |
// v.len = c.r + b.r | |
// b.moveTo(c.add(v)) | |
// } | |
b.update(1 / 60); | |
new Shape(width / 2, height / 2, 380 - b.w, 380 - b.h).forceInside(b, ()=>{ | |
b.velocity.x *= -1 | |
}, ()=>{ | |
b.velocity.y *= -1 | |
}); | |
return b; | |
}; | |
function draw() { | |
background(220); | |
b.frame(); | |
c.draw(); | |
ballUpdater(ball); | |
if (c.hover(ball)) fill("red"); | |
ball.draw(); | |
fill(220); | |
p.moveTo(new Vec2(mouseX, height - mouseY)); | |
p.draw(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment