/* diagram.jsx — 構成図(正面から見た模式図。形・縮尺は目安) * 画像は data/images.js(UW_IMG)。キーは将来の差し替え用に分けている: * housing / port_dg(構成図用ポート) / light_side(左右用ストロボ) / light_top(シュー用ストロボ) / arm / clamp * ※ light_side / light_top / port_dg が未用意の間は light / port を流用 */ const ED = window.UWEngine; const IMG0 = window.UW_IMG || {}; /* 構成図用の画像:img/diagram/ に置き、data/diagram_images.js に「製品ID/ファミリー/カテゴリ → ファイル」を書く。 * 未登録の間は従来の画像(data/images.js)を使う。*/ const DGI = window.UW_DG_IMAGES || {}; const dg = (key, fb) => DGI[key] || fb; const IMGS = { housing: dg('default:housing', IMG0.housing), port: dg('default:port', IMG0.port_dg || IMG0.port), lightSide: dg('default:light_side', IMG0.light_side || IMG0.light), lightTop: dg('default:light_top', IMG0.light_top || IMG0.light), arm: dg('default:arm', IMG0.arm), clamp: dg('default:clamp', IMG0.clamp), shoeBall: dg('default:shoeBall', null) }; /* 画像の登録値は「パス文字列」か、取付点つきの {src, ax, ay, w, h}(ストロボ用)。 * ax, ay … 画像内の取付点(ボール中心/YSベースの下端)。画像の幅・高さに対する 0〜1 の比率 * w, h … 構成図上での画像の大きさ(px)。大きさを変えたいときは w と h を同じ比率で変える * 探す順番:製品ID:スロット → family:ファミリー:スロット → 製品ID/family/cat → default:スロット */ const rawFor = (id, slot) => { const i = id ? ED.info(id) : null; if (!i) return IMGS[slot]; const f = ED.FAM_OF && ED.FAM_OF[id]; const m = DGI[id + ':' + slot] || (f && DGI['family:' + f + ':' + slot]) || (slot === 'lightYs' ? null : i.dgImg); return m || IMGS[slot]; }; const imgFor = (id, slot) => { const v = rawFor(id, slot); return v && v.src ? v.src : v; }; /* ストロボ・ライト:YSベースは lightYs、シュー上は lightTop、左右は lightSide。YSベース用の画像がなければ従来の汎用画像を使う */ const lightFor = (id, slot) => { const v = slot === 'lightYs' && !rawFor(id, 'lightYs') ? rawFor(id, 'lightTop') : rawFor(id, slot); return v && v.src ? { src: v.src, a: v } : { src: v, a: null }; }; // YSベース用の画像がなければ、そのストロボの正面向きの画像(取付点つき)をCUBeeと同じ位置に貼る const ARM_LEN = { S: 70, M: 88, L: 112 }; // 有効長が分からないときの目安 /* 大きさの基準=ボール(25.4mm)。アームのボール・ストロボのボール・ハウジングのハンドルのボールを、すべて BALL px にそろえる。 * TG の完成写真は、もともとこの大きさ(ハウジング幅約200px)。 */ const BALL = 33; const PX_MM = BALL / 25.4; // 構成図上のpx/mm(アームの長さ・フロートの寸法もこの縮尺) const HS_ALPHA = 491,HS_OLD = 550; const PREF = { x: 0.521, y: 0.537 }; // αU系の画像でのマウントリング中心(ポート画像の位置はこれを基準に登録してある) // // ミラーレスのハウジング画像(正方形)の一辺。ハンドルのボールが BALL になる大きさ const armLen = (id) => { const d = (ED.DIM || {})[id]; return d && d.effective_mm ? d.effective_mm * PX_MM : ARM_LEN[ED.armSize(id)] * 1.5 || 130; }; /* ミラーレスのシューベース(アクセサリーシューボールベースII)。透過画像(599×530)をハウジングの大きさ(hs)に対する比率で配置。 * x,y,w,h = 画像の左上と大きさ / bx,by = ボール中心(ここからアーム・ストロボが伸びる)。画像は img/diagram/ に置き、diagram_images.js の 'ACC_SHOE_BALLBASE2' に登録。 */ /* TG のシュー部品(OM SYSTEM PTSA-02/アクセサリーシューボールベースII)。画像は製品IDに {src, ax, ay, tx, ty, w, h} で登録。 * ax,ay=ハウジングのシューに載せる点(下端) / tx,ty=ストロボ・アームを載せる点(PTSA-02はツメの付け根、ボールベースはボール中心) * 直付け(RSメインフレームなし)のときは、PT-059 のシューがレンズ中心より右にあるため TG_SHOE_DX だけ右へ寄せる。 * 実機の写真(直付け・RIG経由の両方)から算出:レンズ中心から +173px/ハウジング幅938px → 構成図の幅200で約37px。RIG経由はほぼ中央。*/ const TG_SHOE_DX = 37; const TG_SHOE_PARTS = ['OM_PTSA02', 'ACC_SHOE_BALLBASE2']; /* TG の構成ごとの完成写真(data/diagram_images.js の UW_DG_TG)。構成が match に当てはまる写真があれば四角の模式図の代わりに使う */ const DGTG = window.UW_DG_TG || []; /* 前面の置き場所:何もなければ正面、正面がふさがっていれば上部(RSバヨネットレンズホルダー)。リングライトは常に正面(engine.js の tgFrontLayout) */ const tgLay = (t) => ED.tgFrontLayout(t); const tgHeld = (t) => t.top === 'holder' ? tgLay(t).top : null; // ホルダー(上部)に付けるレンズ const tgKey = (t) => { const lay = tgLay(t); return { rig: t.rig, grips: t.rig === 'frame' && t.multi ? 0 : t.grips, multi: t.rig === 'frame' && !!t.multi, ballBase: t.ballBase, top: t.top === 'holder' ? 'none' : t.top, shoeConv: t.shoeConv, ring: t.ringLight || '', lens: t.ringLight ? '' : lay.front || '', recv: '' }; }; // 受け側(バヨネットマウントベース)は構成図に出さないので、完成写真の選択にも使わない /* 構成に合う完成写真がないときは、素材(土台・グリップ・ボールベース・シュー部品)を重ねて作る(data/diagram_images.js の UW_DG_TGPARTS)。作れないときは null */ const DGP = window.UW_DG_TGPARTS; const tgCompose = (t) => { if (!DGP) return null; const L = t.lenses || [],lay = tgLay(t),frontId = lay.front; const none = t.rig === 'none',multi = t.rig === 'frame' && !!t.multi,key = none ? 'none' : multi ? (t.ballBase === 2 ? 'multib' : t.ballBase === 1 ? 'multib1' : 'multi') : 'frame',base = DGP.base[key]; const g = none || multi ? 0 : t.rig === 'single' ? Math.max(1, t.grips || 1) : t.grips || 0,nb = none ? 0 : t.ballBase || 0; if (!base) return null; const MB = key === 'multib' || key === 'multib1'; // 土台の写真にボールベースが写っている(マルチアングル+ボールベース1個/2個) const gR = g >= 1,gL = g >= 2,bR = !gR && nb >= 1,bL = !gL && nb >= (gR ? 1 : 2); const layers = [{ ...base, open: none ? 'camera' : 'rig' }],A = {}; const add = (p, side) => {layers.push({ src: p.src, x: p.x, y: p.y, w: p.w, h: p.h, open: 'rig' });A[side] = { x: p.bx, y: p.by };}; if (MB) {if (base.R) A.R = Object.assign({}, base.R);if (base.L) A.L = Object.assign({}, base.L);} else if (gR) add(DGP.gripR, 'R');else if (bR) add(DGP.ballR, 'R'); if (!MB) {if (gL) add(DGP.gripL, 'L');else if (bL) add(DGP.ballL, 'L');} // 上部に RSボールベース(UW_DG_TGPARTS.ballT:軸が真上の画像)。台座を上部の取付位置(seat)に載せ、ボール中心を上の取付点にする if (!none && t.top === 'ballbase' && DGP.ballT) { const p = DGP.ballT,st = DGP.seat[key],x = st.x - p.ax * p.w,y = st.y - p.ay * p.h; layers.push({ src: p.src, x, y, w: p.w, h: p.h, open: 'top' });A.T = { x: x + p.bx * p.w, y: y + p.by * p.h }; } const sp = DGP.shoe[t.shoeConv]; // RIG 使用時で上部に何も付いていないとき:上部の取付位置に「+」(押すとシューベースの画面) if (!none && !A.T && !(sp && t.top === 'shoemount')) A.T = { x: DGP.seat[key].x, y: DGP.seat[key].y }; if (sp && (none || t.top === 'shoemount')) { const st = DGP.seat[key],x = st.x - sp.ax * sp.w,y = st.y - sp.ay * sp.h; layers.push({ src: sp.src, x, y, w: sp.w, h: sp.h, open: 'top' });A.T = { x: x + sp.tx * sp.w, y: y + sp.ty * sp.h }; } // 正面:リングライト・レンズの画像(UW_DG_FRONT)。レンズ中心を (400,328) に合わせる const fi = frontId && (window.UW_DG_FRONT || {})[frontId]; if (fi) { const x = 400 - fi.lx * fi.w,y = 328 - fi.ly * fi.h;layers.push({ src: fi.src, x, y, w: fi.w, h: fi.h, clip: fi.clip, open: t.ringLight ? 'ring' : 'lensTg' }); } const bay = t.receiver && t.receiver !== 'screw'; const bayId = (L.indexOf(ED.BY_INCLUDED) >= 0 ? t.receiver === 'RS_BAYBASE' ? 'RS_BAYBASE' : ED.BY_BASE : t.receiver) || ''; const marker = fi || !frontId ? null : { label: t.ringLight ? 'RL1500' : T("レンズ"), open: t.ringLight ? 'ring' : 'lensTg', ring: false }; // バヨネットマウントベース(RS/FIX)は構成図に表示しない return { layers, A, marker }; }; const tgMatch = (t) => { const k = tgKey(t); return DGTG.find((e) => Object.keys(e.match).every((f) => e.match[f] === '*' || e.match[f] === k[f])) || null; }; const SHOE_ID = 'ACC_SHOE_BALLBASE2'; const SHOE_IMG = { x: 0.397, y: 0.097, w: 0.2317, h: 0.205, bx: 0.512, by: 0.15 }; const newB = () => ({ x0: 1e9, y0: 1e9, x1: -1e9, y1: -1e9 }); function bb(b, x, y, w = 0, h = 0) {b.x0 = Math.min(b.x0, x);b.y0 = Math.min(b.y0, y);b.x1 = Math.max(b.x1, x + w);b.y1 = Math.max(b.y1, y + h);} /* 取付点つきのストロボ・ライト画像。split があるときは、本体(境目より上)だけを s 倍に縮小して、ベース(ボール・YS)はそのままの大きさで描く */ function LightImg({ a, src, x, y }) { const w = a.w,h = a.h,sp = a.split; if (!sp) return ; const sy = sp.y,s = sp.s,px = x + sp.cx * w,py = y + sy * h; return ; } function chain({ k, m, start, dir, floats, box, res, onOpen, noRect }) { const out = [],clamps = []; let x = start.x,y = start.y; const up = k === 'T',ysTop = up && res.topMode === 'ys',pts = [{ x, y }]; m.arms.forEach((a, i) => { const Ls = armLen(a); const deg = up ? -90 : dir < 0 ? i % 2 ? 140 : -160 : i % 2 ? 40 : -20; // 上は真上、左右の2本目は斜め下 const th = deg * Math.PI / 180,vx = Math.cos(th),vy = Math.sin(th); const cx = x + vx * Ls / 2,cy = y + vy * Ls / 2,s = Ls / 1.14,rot = deg + 52.3; const am = rawFor(a, 'arm'); // アームごとの画像(S/M/L)。ボール中心 A→B がこの区間の両端にぴったり合うように、拡大・回転して置く if (am && am.src && am.iw) { const k = Ls / Math.hypot(am.bx - am.ax, am.by - am.ay),th0 = Math.atan2(am.by - am.ay, am.bx - am.ax) * 180 / Math.PI; out.push( onOpen('arms')} />); } else out.push( onOpen('arms')} />); const fl = floats && floats[i]; if (fl && fl.length) { const tot = fl.reduce((t, f) => t + ED.floatLen(f) * PX_MM + 2, -2);let px = -tot / 2; out.push( onOpen('floats')}> {fl.map((f, j) => {const l = ED.floatLen(f) * PX_MM,w = ((ED.U[f] || {}).float_width || 70) * PX_MM;const r = ;px += l + 2;return r;})} ); } x += vx * Ls;y += vy * Ls;pts.push({ x, y });bb(box, x - 20, y - 20, 40, 40);bb(box, cx - 24, cy - 24, 48, 48); }); if (m.pid) { const w = up ? 70 : 78,h = w * 166 / 168,bx = 0.32 * w,flip = dir < 0; const lx = flip ? x - (w - bx) : x - bx,ly = y - h + 4; const L = lightFor(m.pid, ysTop ? 'lightYs' : up ? 'lightTop' : 'lightSide'); // 新形式(取付点つき)か従来形式か const flipV = !up && m.arms.length >= 1; // 左右でアームがあるときは、ボールの位置はそのままで上下反転(ボールの下にストロボ) out.push( onOpen(ED.isStrobe(m.pid) ? 'strobe' : 'light')} transform={flipV ? `translate(0 ${2 * y}) scale(1 -1)` : undefined}> {ysTop && !noRect && } {L.a ? : } ); if (L.a) bb(box, x - L.a.ax * L.a.w - 4, (flipV ? y - (1 - L.a.ay) * L.a.h : y - L.a.ay * L.a.h) - 4, L.a.w + 8, L.a.h + 8); else bb(box, Math.min(lx, 2 * x - lx - w), (flipV ? 2 * y - ly - h : ly) - 6, w + 10, h + 8); } else { out.push( onOpen('strobe')}>{T("+")}); bb(box, x - 20, y - 40, 40, 44); } const n = ED.mountUsed(m) ? ysTop ? m.arms.length : m.arms.length + (m.pid ? 1 : 0) : 0; (ysTop ? pts.slice(1) : pts).slice(0, n).forEach((p, i) => clamps.push( onOpen('clamp')} />)); return {out}{clamps}; } function Diagram({ st, res, onOpen }) { const tg = st.system === 'tg',t = st.tg,M = res.st.mounts; const B = { L: newB(), R: newB(), H: newB() },els = [],S = {}; const newHs = !tg && res.housing && DGI[res.housing.housing_id + ':housing'],hx = 250,hy = 230,hsc = !tg && newHs && newHs.src ? newHs : null,hs = tg ? 300 : hsc ? hsc.hs : newHs ? HS_ALPHA : HS_OLD; // hsc=取付点つきで登録したハウジング(Nikon/Canon など。diagram_images.js) // ハウジングの大きさ(ハンドルのボール基準) const ptsa = tg && TG_SHOE_PARTS.includes(t.shoeConv) ? rawFor(t.shoeConv, 'shoeBase') : null,ptImg = ptsa && ptsa.src ? ptsa : null; // シュー部品の画像(登録がなければ従来の四角) const tgm = tg ? tgMatch(t) : null,tgc = tg && !tgm ? tgCompose(t) : null; // 構成に合う完成写真(なければ null → 従来の模式図) if (tgm || tgc) { // TG:完成写真(tgm)か、素材を重ねたもの(tgc)。写真のレンズ中心を模式図のレンズ位置(400,328)に合わせ、ボールの位置は写真から if (tgm) { const W = tgm.w,Hh = tgm.h,x0 = 400 - tgm.lx * W,y0 = 328 - tgm.ly * Hh; els.push( onOpen(t.rig === 'none' ? 'camera' : 'rig')} />); bb(B.H, x0, y0, W, Hh); const P = (a, d) => a ? { x: x0 + a.x * W, y: y0 + a.y * Hh } : Object.assign({ none: true }, d); S.R = P(tgm.R, { x: x0 + W - 10, y: y0 + 0.2 * Hh });S.L = P(tgm.L, { x: x0 + 10, y: y0 + 0.2 * Hh }); const seat = DGP && t.rig !== 'none' ? DGP.seat[t.rig === 'frame' && t.multi ? 'multi' : 'frame'] : null; // RIG の上部(完成写真に何も写っていないとき) S.T = tgm.T ? P(tgm.T) : seat ? { x: seat.x, y: seat.y } : P(null, { x: 400, y: y0 + 4 }); } else { tgc.layers.forEach((l, i) => { if (l.clip) { const id = 'tgclip' + i; // 画像の一部(楕円)だけを使う els.push( onOpen(l.open)}>{[].concat(l.clip).map((c, j) => c.rx ? : )} ); } else els.push( onOpen(l.open)} transform={l.rot ? `rotate(${l.rot} ${l.rx} ${l.ry})` : undefined} />); bb(B.H, l.x, l.y, l.w, l.h);}); const b0 = tgc.layers[0],D = (side, fx, fy) => tgc.A[side] ? Object.assign({}, tgc.A[side]) : { none: true, x: fx, y: fy }; S.R = D('R', b0.x + b0.w - 10, b0.y + 0.2 * b0.h);S.L = D('L', b0.x + 10, b0.y + 0.2 * b0.h);S.T = D('T', 400, b0.y + 4); if (tgc.marker) els.push(tgc.marker.ring ? // バヨネットマウントベース:レンズの外側に黒いリング onOpen(tgc.marker.open)}> {tgc.marker.label} : onOpen(tgc.marker.open)}> {tgc.marker.label}); } const hd = (window.UW_DG_HOLD || {})[tgHeld(t)]; // ホルダーで保持しているレンズ(レンズ中心の真上、フレーム上部) if (hd) { const hx0 = 400 + hd.dx - hd.w / 2,hy0 = 328 + hd.dyb - hd.h; els.push( onOpen('top')} />);bb(B.H, hx0, hy0, hd.w, hd.h); } } else if (tg) { const bx = 300,by = 250,bw = 200,bh = 130; const fronts = (t.ringLight ? 1 : 0) + (t.lenses || []).length; els.push( onOpen('camera')}> PT-059); els.push( onOpen(t.ringLight ? 'ring' : 'lensTg')}> {fronts ? t.ringLight ? 'RL1500' : T("レンズ") : 'M52'}); bb(B.H, bx, by, bw, bh);S.T = { x: bx + bw / 2, y: by - 2 }; if (t.rig !== 'none') { const fy = by + bh + 6,fx0 = bx - 90,fx1 = bx + bw + 90,grips = t.multi ? 2 : t.grips; const gripR = grips >= 1,gripL = grips >= 2; const ballR = !gripR && t.ballBase >= 1,ballL = !gripL && t.ballBase >= (gripR ? 1 : 2); els.push( onOpen('rig')}> {gripR && } {gripL && } {ballR && } {ballL && } ); bb(B.H, fx0, fy - 160, fx1 - fx0, 180); S.R = { x: fx1 - 15, y: gripR ? fy - 158 : fy - 26, none: !gripR && !ballR };S.L = { x: fx0 + 15, y: gripL ? fy - 158 : fy - 26, none: !gripL && !ballL }; } else {S.R = { x: bx + bw + 10, y: by + 30, none: true };S.L = { x: bx - 10, y: by + 30, none: true };} } else { els.push( onOpen('camera')} />); const hasPort = res.items.some((i) => i.group === T("レンズ・ポート") && ED.info(i.id).cat === 'port'); const pcx = hx + 0.52 * hs,pcy = hy + 0.57 * hs,ps = 0.42 * hs; const portId = (res.items.find((i) => i.group === T("レンズ・ポート") && ED.info(i.id).cat === 'port') || {}).id; const pm = hasPort ? rawFor(portId, 'port') : null,pNew = pm && pm.src && pm.w ? pm : null; // ポートは実物の大きさが決まっているので、ハウジングによらず同じ大きさ(ドームは直径ごとに違う) const pw = pNew ? pNew.w * HS_ALPHA : 0,ph = pNew ? pNew.h * HS_ALPHA : 0; els.push(pNew ? newHs ? onOpen('lens')} /> : onOpen('lens')} /> : hasPort ? onOpen('lens')} /> : onOpen('lens')}>{T("+")}); bb(B.H, hx, hy + 0.2 * hs, hs, 0.62 * hs); if (hsc) {S.R = { x: hx + hsc.R.x * hs, y: hy + hsc.R.y * hs };S.L = { x: hx + hsc.L.x * hs, y: hy + hsc.L.y * hs };S.T = { x: hx + hsc.T.x * hs, y: hy + hsc.T.y * hs };} else { S.R = { x: hx + (newHs ? 0.883 : 0.9) * hs, y: hy + (newHs ? 0.285 : 0.28) * hs };S.L = { x: hx + (newHs ? 0.117 : 0.1) * hs, y: hy + (newHs ? 0.285 : 0.28) * hs };S.T = { x: hx + 0.52 * hs, y: hy + 0.3 * hs };} } // シューベース:使っていないときも「+」を表示(押すとシューベース/TGはシューベース、ミラーレスはストロボの「上に追加」) if (tgm || tgc) { // 写真・素材にシュー部品が写っている。使っていないときは「+」だけ if (!S.T.none && !ED.mountUsed(M.T) && t.top !== 'holder') { els.push( onOpen(t.rig !== 'none' && t.top !== 'shoemount' && t.top !== 'ballbase' && t.shoeConv === 'none' ? 'top' : 'strobeTop')}>+); bb(B.H, S.T.x - 20, S.T.y - 40, 40, 44); } } else if (ED.mountUsed(M.T) || res.topMode) { const p = S.T; const sImg = !tg && ED.mountUsed(M.T) ? imgFor(SHOE_ID, 'shoeBall') : null; if (ptImg) { // TG:シュー部品の画像。ハウジングのシューに載せ、ストロボ・アームは取付点(tx,ty)から const px = p.x + (t.rig === 'none' ? TG_SHOE_DX : 0),w = ptImg.w,h = ptImg.h,ix = px - ptImg.ax * w,iy = p.y - ptImg.ay * h; els.push( onOpen('top')} />); bb(B.H, ix, iy, w, h); S.T = { x: ix + ptImg.tx * w, y: iy + ptImg.ty * h }; } else if (sImg) { // ミラーレス:シューベースの画像を表示し、チェーンの始点をボール中心にする const w = SHOE_IMG.w * hs, h = SHOE_IMG.h * hs, ix = hx + SHOE_IMG.x * hs, iy = hy + SHOE_IMG.y * hs; els.push( onOpen('strobeTop')} />); bb(B.H, ix, iy, w, h); S.T = { x: hx + SHOE_IMG.bx * hs, y: hy + SHOE_IMG.by * hs }; } else { els.push( onOpen(tg ? 'top' : 'strobeTop')} />); S.T = { x: p.x, y: p.y - 16 }; } if (!ED.mountUsed(M.T)) { els.push( onOpen('strobeTop')}>+); bb(B.H, S.T.x - 20, S.T.y - 40, 40, 44); } } else { const p = S.T; els.push( onOpen(tg ? 'top' : 'strobeTop')}> +); bb(B.H, p.x - 20, p.y - 48, 40, 50); } let ai = 0;const fl = {}; ED.MOUNTS.forEach((k) => {fl[k] = M[k].arms.map(() => (res.floatAsg || [])[ai++] || []);}); ['R', 'L', 'T'].forEach((k) => { const m = M[k],box = k === 'T' ? B.H : B[k]; if (!ED.mountUsed(m)) { if (k !== 'T' && !S[k].none) {els.push( onOpen('strobe')}>{T("+")});bb(box, S[k].x - 20, S[k].y - 40, 40, 44);} return; } if (S[k].none) els.push(); bb(box, S[k].x - 10, S[k].y - 10, 20, 20); els.push({chain({ k, m, start: S[k], dir: k === 'L' ? -1 : 1, floats: fl[k], box, res, onOpen, noRect: !!ptImg || !!tgm || !!tgc })}); }); // 表示範囲:全体は余白を大きめに取り、小さく全体が見えるように const U0 = newB();[B.L, B.R, B.H].forEach((b) => {if (b.x0 < 1e9) {bb(U0, b.x0, b.y0);bb(U0, b.x1, b.y1);}}); const vb = (b, pad) => { if (b.x0 >= 1e9) return null; let w = b.x1 - b.x0 + pad * 2,h = b.y1 - b.y0 + pad * 2;const cx = (b.x0 + b.x1) / 2,cy = (b.y0 + b.y1) / 2; const ar = 2.1;if (w / h < ar) w = h * ar;else h = w / ar; // 横長に揃える return `${cx - w / 2} ${cy - h / 2} ${w} ${h}`; }; const box = vb(U0, 50) || '0 0 800 380'; return (
{els}
); } Object.assign(window, { Diagram });