سهبعدی و رندر نسل سوم
This content is not available in your language yet.
فاز ۵ پلن. صحنه در
apps/web/src/components/Scene3D.tsxوcomponents/scene/(مسیرهایscene/…ِ پایین همین پوشهاند)؛ رندر درlib/render/،components/RenderPanel.tsxوcomponents/render/(RenderBridge،RenderOptions،SavedRenders).
بوم: SceneCanvas و فهرستِ three
Section titled “بوم: SceneCanvas و فهرستِ three”صحنه در scene/SceneCanvas.tsx رندر میشود، نه Canvas ِ R3F. رفتار همان است — اندازه از
react-use-measure، رویدادِ اشارهگر روی قاب، معلقشدن و خطای صحنه به بیرونِ بوم، پلِ زمینهٔ React و
StrictMode با its-fine — ولی Canvas ِ R3F کلِ فضای نامِ three را ثبت میکند (extend(THREE)) و
هیچ کلاسی از بسته بیرون نمیماند. SceneCanvas با createRoot ِ خودِ R3F فقط THREE_CATALOGUE
(scene/three-catalogue.ts) را ثبت میکند: تکهٔ three از ۲۶۴٫۷ به ۲۲۳٫۵ کیلوبایت (gzip).
عنصرِ تازهٔ JSX (<torusKnotGeometry>) یا مؤلفهٔ تازهٔ drei که عنصری میسازد، باید کلاسش در فهرست
باشد؛ وگرنه در مرورگر «R3F: X is not part of the THREE namespace» میآید. test/three-catalogue.test.ts
برچسبها را از درختِ نحوِ هر TSX و createElement ِ مؤلفههای drei ِ واردشده میخواند و هم کمبود
و هم کلاسِ اضافه را میگیرد.
حالتِ باز (نسل ششم، فاز ۳)
Section titled “حالتِ باز (نسل ششم، فاز ۳)”- حرکت در هندسه است، نه در وب: هر
PartInstanceِ متحرکfrontId(گروهِ چیزهایی که با هم میجنبند) وmotionدارد —swing،slide،follow،fold— وposeFor(part, leaders, t)جای آن را در لحظهٔt(۰ بسته … ۱ باز) میدهد. قراردادِ علامت درgeometry/src/motion.tsنوشته است: چرخشِ مثبت حولِaxisلبهٔ آزاد را به سمتِ اتاق میبرد، و محور از ضربِ برداری ساخته میشود تا علامتِ دستی نتواند دو بار غلط باشد (دربِ نسل پنجم درونِ بدنه باز میشد). خطِ لولا روی صفحهٔ جلوی بدنه و یک ضخامت بیرونِ لبهٔ لولادار است — کارِ بازوی لولای اروپایی، که بی آن دربِ لولای ۱۱۰ درجه گوشهٔ خودش را ۶ میلیمتر درونِ پهلو میبرد. آزمونِ پذیرش:geometry/test/open.test.ts(هر آشپزخانهٔ نمونه، هر نما، هر زاویه، بی برخورد). - چقدر باز در وب میماند:
openness: Map<frontId, 0..1>درScene3D. «درها باز» همه را باز میکند، کلیک روی یک نما فقط همان را (و بارِ دوم میبندد؛ انتخاب دست نمیخورد)، و لغزندهٔ «میزان باز شدن درها» مقدارِ همه است و کلیکهای تکی را پاک میکند. - بازشو در صحنه (فاز ۳.۴ و ۳.۵): دستگیره از ماشینکاری میآید، نه از حدس —
handleSpotدو سوراخِ همان قطعهای را میخواند که مته میخورد (handleHoles، و در دربِ قابدار قیدِ آزاد یا صفحهٔ وسط) و میله را میانِ آن دو میگذارد؛ پس نقشه و قطعهٔ سوراخشده نمیتوانند اختلاف داشته باشند. دستگیرهٔ ریلی سوراخ ندارد و به پهنای نما روی لبه مینشیند. یونیتی که بازشوش دستگیره نیست هیچ میلهای ندارد؛ بهجایش: پروفیل C/L آلومینیومی درونِ همان شکافی که نماها برایش کوتاه شدهاند (channel-instances.ts،metal)، و شیارِ فرزخورده بهصورتِ یک مستطیلِ تیره آنجا که دیده میشود — شیارِ انگشتی روی رویه، J روی خودِ لبه (gripMark). نمای دستگاه با دستگاهش باز میشود: ظرفشویی و فر به پایین، یخچال به پهلو، یک ربعِ دور. - اتاق و متعلقات (۳.۶ تا ۳.۹): یک ماده به ازای هر ظاهر و نه هر قطعه
(
src/scene/material-cache.ts: آشپزخانهٔ نمونه ۹۲ ← ۵)، سایه فقط از پوستهٔ بیرونی، پیشفرضِ دیوار گچِ گرمِ روشن (DEFAULT_FINISHES)، مانع با شکلِ نوعِ خودش و برچسبِ شناور در انتخاب (Obstacles3D)، متعلقاتِ دیوار — پهلو، تاج، قرنیز، پرکن — از همان قطعهای که بریده میشود (accessory-instances.ts؛ برابری در سطحِ پروژه)، و «قاب» با درهای باز (openBoundsOf). - میانپرده در
src/scene/openness.ts، خالص و بی three: ۳۲۰ میلیثانیه، درب باeaseOutCubic(لولای آرامبندِ کاتالوگ) و کشو خطی (ریل چیزی برای میرا کردن ندارد)، و وقتی همه با هم باز میشوند هر کدام ۳۰ میلیثانیه پس از قبلی راه میافتد.prefers-reduced-motionهمان فریم سرِ جا. یکuseFrameدرRoomPartsهمهٔ گروهها را باapplyPoseمینشاند — بی رندرِ دوبارهٔ React.
حلقهٔ رسم، آزادسازی، از دست رفتنِ زمینه (فاز ۷)
Section titled “حلقهٔ رسم، آزادسازی، از دست رفتنِ زمینه (فاز ۷)”frameloop="demand"(۷.۸، V18): صحنه فقط وقتی چیزی عوض شد رسم میشود. r3f با تغییرِ props خودشinvalidateمیکند وOrbitControlsِ drei با رویدادِchange(میرایی هم)؛ هر تغییرِ بیرون از props دستی:Clipping(برشِ رندرکننده)،RenderBridge(پس از رندرِ آفلاین که اندازهٔ بوم را عوض و برگرداند)، و باز شدنِ نماها (یکuseFrameدرRoomPartsبرای کلِ صحنه؛ تا نماها نرسیدهاند فریم میخواهد؛dtسقفِ ۱/۳۰ دارد چون فریمِ اولِ پس از سکون فاصله تا فریمِ قبلی را میگیرد). صحنهٔ ساکن صفر فریم:e2e/scene-idle.spec.tsفراخوانِ رسمِ WebGL را میشمارد.- آزادسازی (V19):
geometry={…}را r3f آزاد نمیکند. هندسهٔ مالِ جزء (صفحهٔ سوراخدار،WallWash) در پاکسازیِ اثرdisposeمیشود؛ انبارهٔpanelGeometryCachedشمارِ کاربر دارد (retainGeometry): آخرین کاربر که رفت، بافرِ کارت گرافیک پس از ریزکار همانجا آزاد میشود (شیء در انباره میماند و three دوباره بارش میکند)، و سرریز (۸۰۰) فقط قدیمیترینهای بیکاربر را از انباره برمیدارد.e2e/scene-idle.spec.tsبافرهای زندهٔ WebGL را پیش و پس از ۵۰ ویرایشِ عرض میشمارد (سنجیده: ۳۰۲ ← ۲۶۴). - بودجهٔ کارایی (۷.۹،
e2e/perf.spec.ts، تنها با--project perf --no-deps): ۴۰ یونیت، p50 و p95 ِ فاصلهٔ فریم در کشیدنِ مرزِ دوبعدی، کشیدن در «هر دو» و چرخاندنِ صحنه؛ آستانه ≈ ۱٫۵ برابرِ سنجیده روی SwiftShader. نشانگرِ روی یونیت در چرخاندن (دکمهٔ فشرده) عوض نمیشود — آشتیِ همهٔ قطعهها در هر گذر چرخش را کند میکرد (p50 ۱۴۲۳ ← ۱۱۴۴، p95 ۳۴۵۴ ← ۲۲۵۷ میلیثانیه). - انبارهٔ سطح با سقف (V20،
lib/textures/index.ts): ۱۶ سطح (≈ ۶۷ مگابایت)، کماستفادهترین آزاد میشود؛ سطحِ هنوز سوار را three از همان بومِ پخته دوباره بار میکند. - «هر دو» ارزان (۷.۸، V17):
Scene3Dپروژه را باuseFrameBatchedحداکثر یک بار در هر فریم میخواند (یک درخواستِ درراه، نه لغو با هر تغییر — وگرنه کشیدنِ پیوسته هرگز به فریم نمیرسید) وprojectScene(project, cat, cache)را یک بار صدا میزند (Roomصحنه را پارامتر میگیرد). انبارهٔcreateSceneCache()ِ همان نما قطعههای هر یونیت را با هویتِ شیءِ یونیت نگه میدارد: immer یونیتِ دستنخورده را همان شیء میگذارد، پس فقط یونیتِ تغییرکردهPartInstanceِ تازه و هندسهٔ تازه میگیرد. - نورِ نوارِ زیرِ کابینت (V21):
lightStrips(project)روی دیوارِ خودش (wallTransform) وstripLightPoints(strips, 4)— همیشه چهار چراغ؛ بی کابینتِ دیواری خاموش (شدتِ صفر)، نه برداشته. - انتخاب در سهبعدی (۷.۱۱، V24): همهٔ
selection(نه فقط انتخابِ اصلی) با تابشِMARK؛ یونیتِ زیرِ نشانگر تابشِ کمتر (Mark = 'pointed'، وضعیتِ همانRoom)؛ کلیک روی جای خالی (onPointerMissedِ r3f، تا ۲ پیکسل جابهجایی) انتخاب را برمیدارد، جز در اندازهگیری. F (scene.focusدر رجیستری، لایهٔscene) با تمرکز درونِ صحنه — قابِ بومtabIndex={-1}است تا کلیک تمرکز را بیاورد —focusPoseمحدودهٔ انتخاب را از همان سوی دوربین در قاب میگذارد. - پایداری (۷.۱۰، V22):
SceneCanvasدر سوار شدنwebglSupported()را میپرسد (زمینهٔ آزمایشی باWEBGL_lose_contextرها میشود) و رویwebglcontextlostخودش جای بومSceneStoppedمیگذارد: «نمای سهبعدی متوقف شد» با «دوباره» (بومِ تازه باkey) و «برو به دوبعدی»؛ بی WebGL «نمای سهبعدی در دسترس نیست» بی «دوباره»؛ تکهٔ تنبلِ صحنه که نرسد (SceneChunk) «نمای سهبعدی بار نشد» با «دوباره» (lazyِ تازه).
نماها (V1)
Section titled “نماها (V1)”scene/presets.ts: شش نمای آماده از محدودهٔ صحنه و نسبت پنل — سهرخ (همان
قابگیری پیشفرض)، روبهرو، بالا، گوشهٔ چپ/راست، «نمای کار» (چشم ۱۶۰ سانت،
۲۲۰ سانت جلوی جلوترین لبهٔ صحنه). اعمال روی همان OrbitControls (applyPose) تا میرایی و
حد فاصله بماند. «ذخیرهٔ نمای فعلی» → project.views[] = { id, name, pos, target }
(متر، مختصات صحنه) و در همان منو فهرست میشود.
هندسهٔ صحنه (C23، C24)
Section titled “هندسهٔ صحنه (C23، C24)”- دستگاهِ یونیت: z از دیوار به بیرون. یونیت با پشتبندش به دیوار تکیه میدهد:
پشتبندِ روکار در
[0, ضخامت]و بدنه از پشتِ آن (depthFromWall=totalDepth)؛ پیشتر پشتبند درونِ دیوار (z منفی) بود. نمای دستگاهِ توکار هم از همان جلوی بدنه. صفحهٔ کابینت از خودِ دیوار اندازه میخورد (ورقِ بازار)، پس پیشآمدگیاش جلوی یونیتِ پشتبنددار به اندازهٔ پشتبند کمتر است — همان که نصب میشود. - چرخش:
sizeِ هر جعبه در دستگاهِ خودش است (x در امتدادِ دیوار) وrotYآن را میچرخاند؛boundsOfوisoBoxهر دو چرخش را میبینند. مرزِ صحنهٔ الشکل همان مرزِ گوشههای چرخیده است (آزمونِwalls.test.ts). - کلافها به ترتیبِ هستهاند:
rail:0جلو،rail:1عقب. - نمای روبهرو از دیوارِ جاری است (
RenderBridgeِwallId): دوربینِ متعامد سرِ گوشهٔ پایین-چپِ دیوار و ۱۲ متر جلوتر در امتدادِ نرمالش (lib/render/elevation.ts)، نورِ اصلی هم از جلوی همان دیوار. ارتفاعِ خروجی از پهنای مهارشده است (outputSize).
برش و اندازه (V2)
Section titled “برش و اندازه (V2)”- لغزندهٔ «برش»: صفحهٔ برش سراسری رندرکننده روی محور z
(
scene/Clipping.tsx،gl.clippingPlanes) — درصدی از عمق صحنه از دیوار به بیرون؛ داخل کشو و پشتبند دیده میشود. - «اندازه»: با روشن بودنش، کلیک روی هر قطعه نقطه میگیرد؛ دو نقطه → خط و برچسب
فاصله به میلیمتر (
scene/Measure.tsxباHtmlاز drei). نقطهٔ سوم از نو.
روکش اتاق (V3)
Section titled “روکش اتاق (V3)”scene/FinishesPanel.tsx روی room.finishes (رنگ دیوار، کف: نوع و رنگ)؛ صحنه
(scene/Room.tsx) گچ دیوار را با همان رنگ و کف را با سطح نوع (چوب → بافت چوب،
سرامیک/سنگ → کوارتز، بتن → خام) میسازد. ویرایشهای پیاپی یک قدم واگرد.
مونتاژ تعاملی (V5)
Section titled “مونتاژ تعاملی (V5)”حالت «مونتاژ» در نوار: assemblySteps(unit, cat) برای یونیت انتخابشده (از راهِ stepsOf ِ
scene/assembly.ts؛ یونیتِ ناساختنی بی گام)؛
rolesUpTo(steps, i) نقشهای سوارشده تا گام جاری را میدهد و بقیهٔ قطعات همان
یونیت شفاف میشوند (PartBox.dimmed). قبلی/بعدی در SceneTools.
دستگاههای توکار پارامتری (V4)
Section titled “دستگاههای توکار پارامتری (V4)”یونیتی که appliance دارد (فر، یخچال، مایکروویو، ماشین ظرفشویی) در صحنه یک
«نمای دستگاه» میگیرد: جعبهای به اندازهٔ دهانهٔ یونیت منهای ده میلیمتر لقی
از هر طرف، هجده میلیمتر جلوی بدنه. هندسه در packages/geometry/src/fixtures.ts
(applianceFixtures) ساخته میشود و ظاهرش در apps/web/src/components/FixturesAppliances.tsx
از یک جدول کوچک میآید: پنجرهٔ شیشهای، دستگیرهٔ افقی یا عمودی، نوار کنترل.
مدل دانلودی و بافت نداریم — آفلاین میمانیم و اندازه از خودِ یونیت میآید.
رندر: لغو، نگهداشتن در پروژه، جلد گزارش (V6)
Section titled “رندر: لغو، نگهداشتن در پروژه، جلد گزارش (V6)”- لغو:
RenderRequest.signalبین دو نمونهٔ انباشت بررسی میشود و باAbortErrorبیرون میآید؛ پنل رندر دکمهٔ «لغو» را هنگام کار نشان میدهد. - نگهداشتن در پروژه:
project.renders(نوعSavedRender) تا سقف شش رندر؛ هر کدام فقطassetIdدارد — بایت درAssetRepoِ مرورگر و درassets/ِ پروندهٔ.darz، که همچنان خودکفاست (data-model.md، «تصویر بیرون از سند»). فهرست با بندانگشتی، «جلد گزارش» و «حذف» زیر تنظیمات رندر میآید. - جلد گزارش:
coverOf(project, url)در بستهٔ گزارش —urlشناسهٔ تصویر را به data URL میرساند (وب از AssetRepo، خط فرمان ازassets/ِ.darz) — رندرِ علامتخورده (یا تازهترین) را بهReportInput.coverمیدهد و صفحهٔ اول گزارش آن را نشان میدهد. وب و CLI هر دو همین را میفرستند.
رندرِ آفلاین و بافرِ نمای تعاملی (۷.۱۳، V26)
Section titled “رندرِ آفلاین و بافرِ نمای تعاملی (۷.۱۳، V26)”- نفس: حلقهٔ انباشت میان دو نمونه با
breathe()ِlib/render/breathe.ts(یک پیامِMessageChannel) نفس میکشد، نهsetTimeout(0)— زمانسنجِ زبانهٔ پنهان به ≥ ۱ ثانیه کُند میشود. - شکوفهٔ نمرهٔ رنگی (
lib/grade.ts) محوش را باblurRgbِlib/render/blur.ts(سه گذرِ جعبهای، σ=۳) روی بومِ یکچهارم میزند؛ctx.filterنیست، چون سافاری تا ۱۸ آن را بیصدا نادیده میگرفت. - نمای تعاملی بی
preserveDrawingBuffer: رندرِ آفلاین بافر را همانجا پس ازgl.renderمیخواند؛ آزمونی که پیکسلِ صحنه را میخواهد عکسِ ترکیبشدهٔ بوم را میگیرد (e2e/theme.spec.ts). - خطاهای بوم و تصویر
DarzErrorبا پیاماند (lib/render/errors.ts)، نه متنِ فارسی.
عکسِ دیوار: Worker، جهتِ EXIF، مهار (۷.۱۲، V25)
Section titled “عکسِ دیوار: Worker، جهتِ EXIF، مهار (۷.۱۲، V25)”- راستسازی در Worker:
lib/rectify.tsفقط ریاضی و پیکسل است (rectifyPixels)؛lib/rectify.worker.tsباOffscreenCanvasمیخواندش و پیکسلهای منبع را برای کشیدنِ بعدیِ گوشه نگه میدارد.Rectifierِlib/rectify-client.tsصفِ «آخری میبرد» با درنگِ ۱۲۰ میلیثانیه است و درخواستهای همکلید (ویرایشگر و بوم) یک کار میشوند؛ بیOffscreenCanvasهمان کار روی نخ اصلی. - پرونده:
checkPhotoFile(components/photo/photo-file.ts) نوع (عکسِ شطرنجی؛ SVG نه) و سقفِ ۳۰ مگابایت را پیش از خواندن میسنجد؛downscaleباcreateImageBitmap(file, { imageOrientation: 'from-image' })جهتِ EXIF را اعمال میکند (e2e/photo.spec.ts). - گوشه: قابِ عکس اشارهگر را میگیرد (
setPointerCapture)؛ پایانِ کشیدن رها کردن یاlostpointercaptureاست، نه خروج از قاب. - مانع از کلیک روی عکس با
clampObstacleCenter(components/photo/place.ts) درونِ دیوار میماند. - برچسبِ نمونههای
packages/geometryپیام است (src/labels.ts)، لبه رندرش میکند.
تطبیق دوربین با عکس (V7، پژوهشی)
Section titled “تطبیق دوربین با عکس (V7، پژوهشی)”apps/web/src/lib/pose.ts از چهار گوشهٔ کالیبراسیون (photo.corners) و
مستطیل مرجع (photo.reference) هموگرافی دیوار→عکس را با همان solveHomography ِ هسته
میسازد (حلکنندهٔ دوم رفت) و با فرض
فاصلهٔ کانونی، به وضعیت دوربین تجزیه میکند (H = K [r1 r2 t]، در قرارداد
بینایی ماشین و بعد برگردان به دستگاه دیوار). photoCamera(wall, focal) در
RenderBridge همان را به PerspectiveCamera سهبعدی در مختصات اتاق تبدیل
میکند؛ رندر «سهرخ» با تیک «روی عکس اصلی، با دوربین عکس» با این دوربین و
نسبت تصویرِ عکس گرفته و روی عکس اصلی (نه صافشده) مینشیند.
⚠️ فاصلهٔ کانونی از عکس درنمیآید (EXIF نگه نمیداریم). حدس پیشفرض دوربین
اصلی گوشی است (۲۶ میلیمتر معادل: ضلع بلند × ۲۶ / ۳۶) و لغزندهٔ «فاصلهٔ کانونی»
آن را از ۰٫۵ تا ۱٫۶ برابر میلغزاند. آزمون test/pose.test.ts با دوربین مصنوعی
نشان میدهد بازیابی جا، جهت و زاویهٔ دید دقیق است (خطای بازتصویر زیر ۱۰⁻¹⁰
پیکسل، سقف پذیرفته ۳ پیکسل).
رگرسیون بصری (V8)
Section titled “رگرسیون بصری (V8)”apps/web/e2e/visual.spec.ts از بوم سهبعدی آشپزخانهٔ نمونه در پوستهٔ تیره عکس میگیرد و با
عکس مرجع میسنجد (maxDiffPixelRatio: 0.03). عکسِ مرجع فقط در تصویر رسمیِ Playwright
در CI معتبر است و همانجا مقایسه میشود؛ نبودنش شکست است، و عکسِ تازه از کارِ دستیِ
snapshots میآید (testing.md). تا نسل چهارم فقط عکسِ مک
کامیت بود و آزمون بی عکسِ سکوی خودش مینوشتش و پاس میشد: هرگز مقایسه نکرده بود.