tarinai/js/item_render_helpers.js
2026-07-01 14:41:05 +09:00

87 lines
3.1 KiB
JavaScript

"use strict";
// Shared item rendering helpers. Field rendering stays in item_render_runtime.js.
function segmentPathCacheKey(item, segments, extra = "") {
const version = Number(item?._mechanicalShapeVersion || 0) || 0;
const len = Array.isArray(segments) ? segments.length : 0;
let h = len * 2654435761;
if (Array.isArray(segments)) {
const step = Math.max(1, Math.floor(len / 24));
for (let i = 0; i < len; i += step) {
const seg = segments[i];
if (!Array.isArray(seg)) continue;
for (let j = 0; j < 4; j += 1) {
h ^= Math.round((Number(seg[j]) || 0) * 10) & 0xffff;
h = Math.imul(h, 16777619);
}
}
}
return `${item?.type || ""}|${version}|${len}|${h >>> 0}|${extra}`;
}
function getSegmentPath(item, segments, extra = "") {
if (typeof Path2D === "undefined" || !Array.isArray(segments)) return null;
const key = segmentPathCacheKey(item, segments, extra);
const cache = item._segmentRenderPathCache || (item._segmentRenderPathCache = Object.create(null));
if (cache.key === key && cache.path) return cache.path;
const path = new Path2D();
for (const seg of segments) {
if (!Array.isArray(seg) || seg.length < 4) continue;
path.moveTo(Number(seg[0]) || 0, Number(seg[1]) || 0);
path.lineTo(Number(seg[2]) || 0, Number(seg[3]) || 0);
}
cache.key = key;
cache.path = path;
return path;
}
function strokeSegments(ctx, item, segments, extra = "") {
const path = getSegmentPath(item, segments, extra);
if (path) {
ctx.stroke(path);
return;
}
ctx.beginPath();
for (const seg of segments || []) {
if (!Array.isArray(seg) || seg.length < 4) continue;
ctx.moveTo(Number(seg[0]) || 0, Number(seg[1]) || 0);
ctx.lineTo(Number(seg[2]) || 0, Number(seg[3]) || 0);
}
ctx.stroke();
}
function mechanicalRenderTier() {
return window.TarinaiPerf.renderQualityTier();
}
function itemShadowQualityTier() {
return window.TarinaiPerf?.shadowQualityTier?.() || "high";
}
function itemShadowsEnabled() {
return itemShadowQualityTier() !== "off";
}
function drawRopeParticlePath(ctx, particles, ox, oy) {
if (!Array.isArray(particles) || particles.length < 2) return false;
const tier = mechanicalRenderTier();
const stride = tier === "low" && particles.length > 10 ? 2 : 1;
ctx.moveTo((Number(particles[0].x) || 0) - ox, (Number(particles[0].y) || 0) - oy);
for (let i = stride; i < particles.length - 1; i += stride) {
ctx.lineTo((Number(particles[i].x) || 0) - ox, (Number(particles[i].y) || 0) - oy);
}
const last = particles[particles.length - 1];
ctx.lineTo((Number(last.x) || 0) - ox, (Number(last.y) || 0) - oy);
return true;
}
function drawRopeRuntimePath(ctx, particles, item, pb, ax, ay, bx, by) {
if (particles && drawRopeParticlePath(ctx, particles, item.x, item.y)) return true;
const midX = Number(pb?.linkScalar?.(item, "midX", NaN));
const midY = Number(pb?.linkScalar?.(item, "midY", NaN));
const mx = Number.isFinite(midX) ? midX - item.x : (ax + bx) * 0.5;
const my = Number.isFinite(midY) ? midY - item.y : (ay + by) * 0.5;
ctx.moveTo(ax, ay);
ctx.quadraticCurveTo(mx, my, bx, by);
return true;
}