オープンソースなベクトルグラフィックESモジュール「
Paper-es forked Paper.js」を使って、シンプルな式と小さいデータ量で美しい曲線が描ける「
ベジェ曲線」の編集ツールを
Paper.jsのサンプルから移植。
「
hit-testing - Paper-es」
いろんな大きさのベジェ曲線によるカタチをマウスやタップで自由に動かしたり、カタチを変形させたりできます。hit-testing(当たり判定)のデモで、ちゃんとクリックしたところにあるカタチや、線を認識しているのがいい感じです。オープンソースなので、気になる人はアルゴリズムをチェックすることもできます。
直感的に違和感なく使う裏にあるプログラムによるシクミ。おもしろいですね!
JavaScriptのプログラムはこちら。ES-Jamに貼り付けて遊んでみましょう。
<!DOCTYPE html><html lang="ja"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width">
<title>hit-testing - Paper-es</title>
<style>
body {
margin: 0;
height: 100vh;
text-align: center;
}
canvas {
display: block;
width: 100%;
height: 80%;
background-color: #eee;
}
</style>
</head>
<body>
<h1>hit-testing - Paper-es</h1>
<canvas id="canvas" resize></canvas>
<button id=btnadd>add more</button>
<label><input type="checkbox" id=chkremove>remove</label>
<script type="module">
import { Paper, Tool, Path, Rectangle, Point } from "https://code4fukui.github.io/Paper-es/Paper.js";
Paper.install(window); // -> activate project, tool, view
Paper.setup("canvas");
const values = {
paths: 20,
minPoints: 5,
maxPoints: 15,
minRadius: 30,
maxRadius: 500,
};
const hitOptions = {
segments: true,
stroke: true,
fill: true,
tolerance: 5,
};
const createBlob = (center, maxRadius, points) => {
const path = new Path();
path.closed = true;
for (let i = 0; i < points; i++) {
const delta = new Point({
length: (maxRadius * 0.5) + (Math.random() * maxRadius * 0.5),
angle: (360 / points) * i
});
path.add(center.add(delta));
}
path.smooth();
return path;
};
const createPaths = () => {
const radiusDelta = values.maxRadius - values.minRadius;
const pointsDelta = values.maxPoints - values.minPoints;
for (let i = 0; i < values.paths; i++) {
const radius = values.minRadius + Math.pow(Math.random(), 5) * radiusDelta;
const points = values.minPoints + Math.floor(Math.random() * pointsDelta);
const path = createBlob(view.size.multiply(Point.random()), radius, points);
const lightness = .7; // (Math.random() - 0.5) * 0.4 + 0.4;
const hue = Math.random() * 360;
const saturation = .5;
path.fillColor = { hue, saturation, lightness };
path.strokeColor = "black";
};
};
createPaths();
btnadd.onclick = () => createPaths();
let segment, path;
let movePath = false;
view.onMouseDown = (event) => {
segment = path = null;
const hitResult = project.hitTest(event.point, hitOptions);
if (!hitResult) {
return;
}
if (event.modifiers.shift || chkremove.checked) {
if (hitResult.type == "segment") {
hitResult.segment.remove();
} else if (hitResult.type == "fill") {
hitResult.item.remove();
}
return;
}
if (hitResult) {
path = hitResult.item;
if (hitResult.type == 'segment') {
segment = hitResult.segment;
} else if (hitResult.type == 'stroke') {
const location = hitResult.location;
segment = path.insert(location.index + 1, event.point);
path.smooth();
}
}
movePath = hitResult.type == "fill";
if (movePath) {
project.activeLayer.addChild(hitResult.item);
}
};
view.onMouseMove = (event) => {
project.activeLayer.selected = false;
if (event.item) {
event.item.selected = true;
}
};
view.onMouseDrag = (event) => {
if (segment) {
segment.point.x += event.delta.x;
segment.point.y += event.delta.y;
path.smooth();
} else if (path) {
path.position.x += event.delta.x;
path.position.y += event.delta.y;
}
}
</script>
</body>
</html>
(src on GitHub, forked Paper.js - Hit Testing)