@koda-sl/baker-cli 0.534.0-dev.911a80433 → 0.534.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/account-2UXZ6CZJ.js +82 -0
- package/dist/account-OWMJG4IW.js +82 -0
- package/dist/account-reads-W2GAN335.js +219 -0
- package/dist/account-users-WLTQNPL7.js +64 -0
- package/dist/account-writes-TNADUSHW.js +68 -0
- package/dist/accounts-D3SQTZB7.js +701 -0
- package/dist/accounts-HRQFOTBJ.js +80 -0
- package/dist/actions-VJXED57L.js +55 -0
- package/dist/activecampaign-TNJIQA4C.js +938 -0
- package/dist/ad-group-writes-7BGCZA3M.js +172 -0
- package/dist/ad-groups-A6GU6EW5.js +70 -0
- package/dist/ad-writes-CEMCUDU2.js +176 -0
- package/dist/add-DRTKSLEQ.js +107 -0
- package/dist/add-LBHISK44.js +305 -0
- package/dist/ads-4EVKVAMR.js +48 -0
- package/dist/ads-OY455VCX.js +79 -0
- package/dist/advertisers-G6DZRBOK.js +123 -0
- package/dist/analytics-ANG42PKM.js +2437 -0
- package/dist/analytics-UI4AKUSF.js +447 -0
- package/dist/animate-KCSICY2N.js +480 -0
- package/dist/assign-5DFH6HBV.js +80 -0
- package/dist/audience-size-P6WIXOB3.js +76 -0
- package/dist/audiences-4ASLCNOF.js +280 -0
- package/dist/audit-LA4BSLXV.js +154 -0
- package/dist/autocomplete-XMG6E7TG.js +114 -0
- package/dist/avatars-4IFN4GZD.js +38 -0
- package/dist/bid-pricing-JEOG2Q3L.js +103 -0
- package/dist/brand-C6HUAK74.js +22 -0
- package/dist/brief-5BCPPBU3.js +543 -0
- package/dist/campaign-groups-IYQZSICZ.js +99 -0
- package/dist/campaign-writes-CVOOPZ35.js +176 -0
- package/dist/campaign-writes-YXI5UOPH.js +168 -0
- package/dist/campaigns-FW4WVD25.js +113 -0
- package/dist/campaigns-QMFHNG3N.js +111 -0
- package/dist/campaigns-TX37WHVL.js +78 -0
- package/dist/canvas-U7MZXZKR.js +46 -0
- package/dist/capabilities-7643Y2HU.js +147 -0
- package/dist/catalog-FLWVVX2U.js +66 -0
- package/dist/catalogs-YNIUP2AH.js +130 -0
- package/dist/changes-QUKBZVB3.js +135 -0
- package/dist/chatgpt-TIUBMATF.js +47 -0
- package/dist/chats-BGLAG4Q7.js +279 -0
- package/dist/check-MN5ZQ7RX.js +107 -0
- package/dist/chunk-2UJ5ABI6.js +604 -0
- package/dist/chunk-2XRFWXSA.js +103 -0
- package/dist/chunk-3OSBCYKA.js +8954 -0
- package/dist/chunk-3QQHPRRB.js +89 -0
- package/dist/chunk-3WTCMBDC.js +1518 -0
- package/dist/chunk-44ZRCSR7.js +228 -0
- package/dist/chunk-46YLDIQG.js +4254 -0
- package/dist/chunk-46YLDIQG.js.map +1 -0
- package/dist/chunk-4QITYZUH.js +163 -0
- package/dist/chunk-4XNF52NU.js +429 -0
- package/dist/chunk-4Y7IJ2PU.js +5368 -0
- package/dist/chunk-53BTYIMJ.js +91 -0
- package/dist/chunk-56OEYUJU.js +61 -0
- package/dist/chunk-5N47LB7X.js +285 -0
- package/dist/chunk-5UBFYJHF.js +170 -0
- package/dist/chunk-6PMJU7IU.js +169 -0
- package/dist/chunk-7I65KXU7.js +98 -0
- package/dist/chunk-BXUUKRMM.js +46 -0
- package/dist/chunk-BZTAEZLT.js +51 -0
- package/dist/chunk-CJQ77HO5.js +1646 -0
- package/dist/chunk-CKTVRR3D.js +57 -0
- package/dist/chunk-COS3TPHX.js +377 -0
- package/dist/chunk-CQ6HBFOB.js +23 -0
- package/dist/chunk-DWW5QSUF.js +638 -0
- package/dist/chunk-ENUCJ6AO.js +148 -0
- package/dist/chunk-ES3PWGS3.js +438 -0
- package/dist/chunk-FJDSL4O5.js +910 -0
- package/dist/chunk-FSTDWVXG.js +455 -0
- package/dist/chunk-FUR7R4D7.js +249 -0
- package/dist/chunk-HTT7RBRC.js +846 -0
- package/dist/chunk-HVJJEQDQ.js +7234 -0
- package/dist/chunk-I3LRC33M.js +5471 -0
- package/dist/chunk-I7AYMJZA.js +783 -0
- package/dist/chunk-IU5K42Q3.js +831 -0
- package/dist/chunk-JAC6JVCI.js +26 -0
- package/dist/chunk-JEUFRIYS.js +297 -0
- package/dist/chunk-KITVYLEX.js +843 -0
- package/dist/chunk-L4LNS54Y.js +138 -0
- package/dist/chunk-L4LNS54Y.js.map +1 -0
- package/dist/chunk-L5VVKIMB.js +203 -0
- package/dist/chunk-M2MJ52HS.js +676 -0
- package/dist/chunk-MBSAHNMJ.js +547 -0
- package/dist/chunk-MCJ5I5LR.js +3187 -0
- package/dist/chunk-MCJ5I5LR.js.map +1 -0
- package/dist/chunk-MHZIOOT5.js +197 -0
- package/dist/chunk-MIRPLAFD.js +1902 -0
- package/dist/chunk-MNOCC5AL.js +207 -0
- package/dist/chunk-MZPS46OR.js +76 -0
- package/dist/chunk-NZUMNTAT.js +3510 -0
- package/dist/chunk-OXLB5M5M.js +592 -0
- package/dist/chunk-OYNBUT5X.js +479 -0
- package/dist/chunk-PIAG4UJT.js +274 -0
- package/dist/chunk-PV2JVXFM.js +66 -0
- package/dist/chunk-QKNPF6ZD.js +24026 -0
- package/dist/chunk-QKNPF6ZD.js.map +1 -0
- package/dist/chunk-QOBA6PHB.js +131 -0
- package/dist/chunk-R6WEPF63.js +19 -0
- package/dist/chunk-RQB4XLSF.js +2209 -0
- package/dist/chunk-RRC7F3QQ.js +1919 -0
- package/dist/chunk-S6VFDNSQ.js +63 -0
- package/dist/chunk-SF52UGHZ.js +225 -0
- package/dist/chunk-SG2TDQEE.js +150 -0
- package/dist/chunk-SWFZJROM.js +64 -0
- package/dist/chunk-T33M3PK7.js +930 -0
- package/dist/chunk-TNKKK7XD.js +224 -0
- package/dist/chunk-TNOOWNN7.js +91 -0
- package/dist/chunk-UAB4CYBH.js +191 -0
- package/dist/chunk-UBDY5RE4.js +387 -0
- package/dist/chunk-UCNIJ2IW.js +494 -0
- package/dist/chunk-UFUYTWEF.js +344 -0
- package/dist/chunk-UROKPFIS.js +142 -0
- package/dist/chunk-VP4PO7FW.js +392 -0
- package/dist/chunk-WRTG3KXU.js +156 -0
- package/dist/chunk-WWLZ5RRU.js +319 -0
- package/dist/chunk-YE4RCEEJ.js +167 -0
- package/dist/chunk-YO54MA7R.js +275 -0
- package/dist/claim-2JNNC66N.js +200 -0
- package/dist/cli.js +32 -32
- package/dist/companies-4NJU5TLU.js +198 -0
- package/dist/company-QOYWS4W6.js +308 -0
- package/dist/complete-7FX2K3PV.js +84 -0
- package/dist/connect-H2HSPMRT.js +45 -0
- package/dist/conversation-4M7YMQOL.js +96 -0
- package/dist/conversion-report-G5JMZNYV.js +127 -0
- package/dist/conversion-upload-command-43PZFYT7.js +439 -0
- package/dist/conversion-writes-JQQCCGPO.js +122 -0
- package/dist/conversions-UO4CQNF6.js +78 -0
- package/dist/conversions-Z7FW7CFT.js +140 -0
- package/dist/create-4SFO4AQZ.js +62 -0
- package/dist/create-D5MA64KL.js +239 -0
- package/dist/create-GJ4ZMY3A.js +210 -0
- package/dist/create-QVY6DSYM.js +135 -0
- package/dist/creatives-NTAKM5TG.js +58 -0
- package/dist/creatives-R3XILT6D.js +223 -0
- package/dist/credentials-EQRB6XGZ.js +290 -0
- package/dist/critique-4YRRUNAU.js +1451 -0
- package/dist/critique-JCU2ATT6.js +232 -0
- package/dist/crm-AF6XOOSF.js +323 -0
- package/dist/crm-LSMFGR6R.js +227 -0
- package/dist/currency-4KNOOZUU.js +88 -0
- package/dist/customers-MM3VIZNA.js +59 -0
- package/dist/delete-CO2YBPTI.js +67 -0
- package/dist/delete-D7HTW32U.js +57 -0
- package/dist/delete-JUPBYK3H.js +69 -0
- package/dist/demographics-DK5JDY6M.js +141 -0
- package/dist/discard-DEJOM62Y.js +69 -0
- package/dist/discover-U6YLVLTF.js +172 -0
- package/dist/domain-MBOMV3CQ.js +101 -0
- package/dist/download-KN2I55SA.js +172 -0
- package/dist/draft-BMNBT37B.js +283 -0
- package/dist/draft-CGPVRRKC.js +335 -0
- package/dist/draft-F74OIT5O.js +310 -0
- package/dist/draft-FIEZWBIX.js +308 -0
- package/dist/draft-HZ5T6NOI.js +220 -0
- package/dist/engine/index.js +8 -8
- package/dist/experiments-YNJZCMQM.js +236 -0
- package/dist/facets-Y4OACBMC.js +117 -0
- package/dist/feedback-I5FZZAJR.js +115 -0
- package/dist/feedback-record-U2O24ITU.js +269 -0
- package/dist/fetch-XU6QVWMS.js +179 -0
- package/dist/fit-IHUUFCGI.js +65 -0
- package/dist/flows-WSCIMXQX.js +250 -0
- package/dist/fonts-3QW7EDHE.js +39 -0
- package/dist/forecast-2WVHMSZP.js +169 -0
- package/dist/forecast-F25LSCM5.js +121 -0
- package/dist/funnel-35DYDBJH.js +397 -0
- package/dist/generate-Z7AROAES.js +263 -0
- package/dist/get-4CAX33L2.js +59 -0
- package/dist/get-N7X75ZJO.js +82 -0
- package/dist/google-M6XR7YGT.js +3267 -0
- package/dist/google-ads-NCSMNNND.js +198 -0
- package/dist/gsc-GQFI6I2D.js +30 -0
- package/dist/history-FGQ2H7XH.js +69 -0
- package/dist/history-LTOFKBMJ.js +136 -0
- package/dist/history-VJ254UXF.js +102 -0
- package/dist/hubspot-W2NM75YK.js +782 -0
- package/dist/ideas-54KFDNDM.js +45 -0
- package/dist/images-JOEVCCP6.js +92 -0
- package/dist/ingest-6R7XUL46.js +27 -0
- package/dist/ingest-VCIH5QDR.js +123 -0
- package/dist/insights-UXUUOQB7.js +132 -0
- package/dist/inspect-MGOR77FP.js +192 -0
- package/dist/intent-FZWPRUCI.js +95 -0
- package/dist/keyword-gap-4ECYPWXJ.js +130 -0
- package/dist/keyword-metrics-SBIRN5KC.js +200 -0
- package/dist/keywords-for-site-TLNPJHT2.js +131 -0
- package/dist/landing-ZICSVWKF.js +28 -0
- package/dist/languages-VNZQ5USL.js +40 -0
- package/dist/leads-JUQFNK55.js +115 -0
- package/dist/learn-A6BT25LQ.js +207 -0
- package/dist/line-item-writes-NW7A6HNH.js +211 -0
- package/dist/line-items-OAG3QMDE.js +107 -0
- package/dist/link-4VJ7FSOA.js +67 -0
- package/dist/linkedin-CKOWKD4M.js +1328 -0
- package/dist/linkedin-person-C22IDK7S.js +139 -0
- package/dist/list-2DKLO6AO.js +67 -0
- package/dist/list-CDDXHMJQ.js +306 -0
- package/dist/list-ZORO7PFP.js +154 -0
- package/dist/locations-4RDUHKVR.js +69 -0
- package/dist/locations-HATR6PSC.js +40 -0
- package/dist/log-OSJHKHPV.js +95 -0
- package/dist/make-JPXGF44U.js +1367 -0
- package/dist/map-PMGZDXAN.js +436 -0
- package/dist/mcp-MOOOTRYG.js +729 -0
- package/dist/media-sources-5OYCKQ5J.js +35 -0
- package/dist/members-QG5IP5HM.js +47 -0
- package/dist/metrics-R4YS2JL6.js +152 -0
- package/dist/models-RJMZV5SH.js +155 -0
- package/dist/normalize-LATTXSL3.js +144 -0
- package/dist/options-KALH5TB7.js +125 -0
- package/dist/organization-AGCF7DUJ.js +949 -0
- package/dist/performance-SITVZQWX.js +5565 -0
- package/dist/performance-YNXPUSGJ.js +27 -0
- package/dist/pixels-TYJVE377.js +62 -0
- package/dist/promoted-tweet-writes-NAHPJ3VD.js +183 -0
- package/dist/promoted-tweets-2XQ4MWAC.js +119 -0
- package/dist/publish-UITYPUBD.js +344 -0
- package/dist/query-JOYRNM4O.js +231 -0
- package/dist/query-JOYRNM4O.js.map +1 -0
- package/dist/query-RE7NB2BK.js +62 -0
- package/dist/reddit-6XIARCTX.js +29 -0
- package/dist/refresh-6UCN7PDX.js +97 -0
- package/dist/release-IN65L23O.js +63 -0
- package/dist/relevant-pages-W7IM6YQW.js +99 -0
- package/dist/remove-X3XWVE2M.js +53 -0
- package/dist/reopen-RMMQ55CH.js +66 -0
- package/dist/rerun-HCGLAUYW.js +96 -0
- package/dist/research-LZBPCXIW.js +72 -0
- package/dist/research-W5KUOBXZ.js +265 -0
- package/dist/reset-MUWXTLIB.js +57 -0
- package/dist/reset-VQNKXFAF.js +57 -0
- package/dist/resolve-DMGWLMTC.js +124 -0
- package/dist/retry-YI4YLYTQ.js +63 -0
- package/dist/retry-ZQUJPKQH.js +65 -0
- package/dist/revenue-6YZAOMF6.js +91 -0
- package/dist/rules-FHVZEN4D.js +99 -0
- package/dist/rules-add-7X46VTIJ.js +465 -0
- package/dist/run-2CSG6NIY.js +71 -0
- package/dist/run-TA6HYFKZ.js +69 -0
- package/dist/scaffold-ad-AQK4PX7C.js +1513 -0
- package/dist/scaffold-static-ad-ORMMKUWY.js +1011 -0
- package/dist/scaffold-video-525OYZYI.js +885 -0
- package/dist/schedule-TMTLYULK.js +64 -0
- package/dist/scheduled-agents-TS3WLRCM.js +55 -0
- package/dist/schema-6S5ZEPBS.js +135 -0
- package/dist/search-LTUUAGZG.js +177 -0
- package/dist/select-XAC7G4ZT.js +110 -0
- package/dist/set-prompt-H3UEDJWS.js +199 -0
- package/dist/setup-links-6ANVKZCE.js +144 -0
- package/dist/shopify-6ISTKZCA.js +3958 -0
- package/dist/site-OIV55MGO.js +180 -0
- package/dist/sitemaps-VYLDUI4L.js +77 -0
- package/dist/sitemaps-VYLDUI4L.js.map +1 -0
- package/dist/sites-HM7ZCGCZ.js +97 -0
- package/dist/sites-HM7ZCGCZ.js.map +1 -0
- package/dist/sources-5L4S5BQV.js +457 -0
- package/dist/sources-GLJWZFHZ.js +30 -0
- package/dist/stale-TPLB2P4E.js +90 -0
- package/dist/status-TD7ISXC5.js +65 -0
- package/dist/studio-IDNM42JP.js +49 -0
- package/dist/sync-PVIFUXX3.js +57 -0
- package/dist/sync-definition-K5TJORL5.js +70 -0
- package/dist/tags-3HB7N5WB.js +23 -0
- package/dist/tags-DG3XQ4SF.js +227 -0
- package/dist/tags-T4EN6XYE.js +23 -0
- package/dist/tags-T6THTVNP.js +26 -0
- package/dist/tags-XY6R2VJ6.js +23 -0
- package/dist/targeting-criteria-CL472KJO.js +113 -0
- package/dist/targets-7AQMSO6H.js +106 -0
- package/dist/testimonials-4QWVSEPU.js +32 -0
- package/dist/themes-ZTM4OVGK.js +95 -0
- package/dist/top-companies-PMFYOAFK.js +111 -0
- package/dist/transcript-YWFYNFWX.js +165 -0
- package/dist/translate-R2DXDXUY.js +603 -0
- package/dist/triggers-JKXISN45.js +144 -0
- package/dist/unlink-IUAFDLBT.js +70 -0
- package/dist/unstage-ZYT5RNLK.js +88 -0
- package/dist/update-23Q4T2J5.js +155 -0
- package/dist/update-74VME6PN.js +66 -0
- package/dist/update-O22DAZVX.js +192 -0
- package/dist/update-QAWK3O7L.js +101 -0
- package/dist/upload-WASRCKS4.js +130 -0
- package/dist/url-options-M7RX2TD4.js +131 -0
- package/dist/url-params-E5ON6W2N.js +101 -0
- package/dist/validate-FGETFHYN.js +125 -0
- package/dist/videos-SS2Y6FJF.js +41 -0
- package/dist/viewer-check-CWS6OVM7.js +83 -0
- package/dist/web-HT22JFQR.js +143 -0
- package/dist/winning-ads-FXTP4PWZ.js +86 -0
- package/dist/write-commands-5YVQKO7E.js +1525 -0
- package/dist/x-KEMAY75C.js +47 -0
- package/package.json +1 -1
- package/dist/account-BRRFMDXZ.js +0 -82
- package/dist/account-MQI7EZBH.js +0 -82
- package/dist/account-reads-K6YDAOHX.js +0 -219
- package/dist/account-users-QS54ZEOD.js +0 -64
- package/dist/account-writes-PGGGP26M.js +0 -68
- package/dist/accounts-H3U7LARM.js +0 -80
- package/dist/accounts-IW62WBX5.js +0 -701
- package/dist/actions-2CUQFZNA.js +0 -55
- package/dist/activecampaign-WAMDVBUO.js +0 -938
- package/dist/ad-group-writes-ROUU6BHU.js +0 -172
- package/dist/ad-groups-Y2BSRLIN.js +0 -70
- package/dist/ad-writes-NF4LCGVP.js +0 -176
- package/dist/add-4P3CUHAN.js +0 -305
- package/dist/add-UHWRBSFE.js +0 -107
- package/dist/ads-CPMX3L4A.js +0 -79
- package/dist/ads-TDN7OYSU.js +0 -48
- package/dist/advertisers-6CHV5N2V.js +0 -123
- package/dist/analytics-4GTUW42V.js +0 -2437
- package/dist/analytics-MV2RQ6KR.js +0 -447
- package/dist/animate-EOSLSA7F.js +0 -480
- package/dist/assign-RN5DGDS2.js +0 -80
- package/dist/audience-size-7DYMCXDM.js +0 -76
- package/dist/audiences-VMGB2JZG.js +0 -280
- package/dist/audit-7U7G2JKS.js +0 -154
- package/dist/autocomplete-E6YHJ3HH.js +0 -114
- package/dist/avatars-6JHVTSXH.js +0 -38
- package/dist/bid-pricing-J3OJK4J3.js +0 -103
- package/dist/brand-3EAQFDYN.js +0 -22
- package/dist/brief-ADGCGC3H.js +0 -543
- package/dist/campaign-groups-GN7D5UXE.js +0 -99
- package/dist/campaign-writes-HKWI6GYU.js +0 -168
- package/dist/campaign-writes-UORRIIWY.js +0 -176
- package/dist/campaigns-5I76AV7F.js +0 -113
- package/dist/campaigns-BEXDLBT3.js +0 -111
- package/dist/campaigns-GONROKTU.js +0 -78
- package/dist/canvas-CHV34G62.js +0 -46
- package/dist/capabilities-VKBIQ2BR.js +0 -147
- package/dist/catalog-VXANSX2O.js +0 -66
- package/dist/catalogs-LXEM7NNC.js +0 -130
- package/dist/changes-4OYC4NMW.js +0 -135
- package/dist/chatgpt-XV6CVKLN.js +0 -47
- package/dist/chats-W35ZPZEW.js +0 -279
- package/dist/check-3D63D4ZT.js +0 -107
- package/dist/chunk-2PC26XRF.js +0 -592
- package/dist/chunk-2UZHRLLO.js +0 -148
- package/dist/chunk-45EERHDB.js +0 -783
- package/dist/chunk-55B4AJY3.js +0 -249
- package/dist/chunk-56UWXQEN.js +0 -392
- package/dist/chunk-5G5QFFJY.js +0 -275
- package/dist/chunk-5GHKZLID.js +0 -438
- package/dist/chunk-5HGAO4ZI.js +0 -89
- package/dist/chunk-5YN53OAA.js +0 -61
- package/dist/chunk-6CCEEVXJ.js +0 -285
- package/dist/chunk-6HGLQJ6A.js +0 -638
- package/dist/chunk-6OIKNJ7G.js +0 -26
- package/dist/chunk-6OXENBTF.js +0 -74
- package/dist/chunk-6OXENBTF.js.map +0 -1
- package/dist/chunk-6QGBBX7F.js +0 -46
- package/dist/chunk-6RTZPBAT.js +0 -19
- package/dist/chunk-7MY3QHGA.js +0 -203
- package/dist/chunk-AH6TDWYS.js +0 -3510
- package/dist/chunk-BKOUVU6X.js +0 -547
- package/dist/chunk-CBQPKCFE.js +0 -170
- package/dist/chunk-CWZ2ODJQ.js +0 -57
- package/dist/chunk-DBPPTM3W.js +0 -24026
- package/dist/chunk-DBPPTM3W.js.map +0 -1
- package/dist/chunk-DGZYZWMK.js +0 -843
- package/dist/chunk-DMA67YC3.js +0 -344
- package/dist/chunk-DT3V5EGL.js +0 -2209
- package/dist/chunk-DYOUJERF.js +0 -7234
- package/dist/chunk-E3FMS5DA.js +0 -297
- package/dist/chunk-EIRGJD3Q.js +0 -167
- package/dist/chunk-F3CZFUXZ.js +0 -3187
- package/dist/chunk-F3CZFUXZ.js.map +0 -1
- package/dist/chunk-G3MEYGXC.js +0 -1518
- package/dist/chunk-G3MN4HIX.js +0 -831
- package/dist/chunk-G53CUQZV.js +0 -207
- package/dist/chunk-GPZVYXJZ.js +0 -455
- package/dist/chunk-HGHVNUJT.js +0 -156
- package/dist/chunk-IZABTTW5.js +0 -228
- package/dist/chunk-J3HA4RFY.js +0 -51
- package/dist/chunk-JGOYO7I2.js +0 -103
- package/dist/chunk-JUF6Q5X7.js +0 -8954
- package/dist/chunk-KAAYVKBH.js +0 -479
- package/dist/chunk-KUM36NSV.js +0 -429
- package/dist/chunk-MHSVWQK6.js +0 -91
- package/dist/chunk-MTV2RSGY.js +0 -63
- package/dist/chunk-MUS2UTWU.js +0 -4254
- package/dist/chunk-MUS2UTWU.js.map +0 -1
- package/dist/chunk-N4BK6OKX.js +0 -91
- package/dist/chunk-NP3FPCPN.js +0 -377
- package/dist/chunk-OAXQPLTG.js +0 -604
- package/dist/chunk-OI2CKYAQ.js +0 -846
- package/dist/chunk-QUYDMAQN.js +0 -169
- package/dist/chunk-RIA5RXPL.js +0 -387
- package/dist/chunk-S556LOAP.js +0 -930
- package/dist/chunk-SIKCVWM3.js +0 -76
- package/dist/chunk-SOS4QFLE.js +0 -5471
- package/dist/chunk-T65Y7N7H.js +0 -225
- package/dist/chunk-T75WL56Z.js +0 -224
- package/dist/chunk-TGCDXZ5X.js +0 -910
- package/dist/chunk-TOUF5ME6.js +0 -1646
- package/dist/chunk-TWCS6FSC.js +0 -191
- package/dist/chunk-UPXPBA4W.js +0 -676
- package/dist/chunk-URMAEVZO.js +0 -150
- package/dist/chunk-UUWUJ64R.js +0 -197
- package/dist/chunk-VGUGFRWF.js +0 -494
- package/dist/chunk-VYJLS64A.js +0 -1902
- package/dist/chunk-WEQZ563U.js +0 -319
- package/dist/chunk-WUCTGDV7.js +0 -163
- package/dist/chunk-XCRYRVKK.js +0 -5368
- package/dist/chunk-XJ5TX772.js +0 -131
- package/dist/chunk-XPIBSDG4.js +0 -274
- package/dist/chunk-Y6MLSN6W.js +0 -1919
- package/dist/chunk-YBFZJ5EB.js +0 -66
- package/dist/chunk-YR3OPPUE.js +0 -142
- package/dist/chunk-YXQFCQX2.js +0 -64
- package/dist/chunk-Z3Y67AH6.js +0 -98
- package/dist/chunk-ZKRRYVDG.js +0 -23
- package/dist/claim-DWGPEVJN.js +0 -200
- package/dist/companies-3VT7OJAA.js +0 -198
- package/dist/company-XEPW45AT.js +0 -308
- package/dist/complete-AEX34ICU.js +0 -84
- package/dist/connect-TF4FCXDV.js +0 -45
- package/dist/conversation-6WWZMPAB.js +0 -96
- package/dist/conversion-report-I6R2FDCD.js +0 -127
- package/dist/conversion-upload-command-QD6MGLY4.js +0 -439
- package/dist/conversion-writes-H2JOAORW.js +0 -122
- package/dist/conversions-D46PQRWF.js +0 -140
- package/dist/conversions-WGW43FUE.js +0 -78
- package/dist/create-7WV7XWZV.js +0 -62
- package/dist/create-OUZZLPQK.js +0 -210
- package/dist/create-QRT6O5PN.js +0 -135
- package/dist/create-WSEYNFTP.js +0 -239
- package/dist/creatives-BSZY7VDQ.js +0 -223
- package/dist/creatives-DBDFDHX7.js +0 -58
- package/dist/credentials-P7GQYIE3.js +0 -290
- package/dist/critique-6F4FWQN5.js +0 -232
- package/dist/critique-X2YDOMON.js +0 -1451
- package/dist/crm-O6YG6777.js +0 -227
- package/dist/crm-VNAUSVC5.js +0 -323
- package/dist/currency-UVT3HEZT.js +0 -88
- package/dist/customers-VJ3TXILQ.js +0 -59
- package/dist/delete-3H377GOM.js +0 -67
- package/dist/delete-EH3XL26Z.js +0 -57
- package/dist/delete-QYSOAVP4.js +0 -69
- package/dist/demographics-GKJZ5PV2.js +0 -141
- package/dist/discard-46RALLBO.js +0 -69
- package/dist/discover-C4TG7JP7.js +0 -172
- package/dist/domain-JNNWJNQE.js +0 -101
- package/dist/download-3R5IHN5F.js +0 -172
- package/dist/draft-3L4SNDGO.js +0 -283
- package/dist/draft-EADHX7XL.js +0 -335
- package/dist/draft-TAM7IUPH.js +0 -220
- package/dist/draft-VSXBSXSX.js +0 -308
- package/dist/draft-XCYSZWA6.js +0 -310
- package/dist/experiments-JVGAKWFA.js +0 -236
- package/dist/facets-JG44XXAT.js +0 -117
- package/dist/feedback-3JCNU6MT.js +0 -115
- package/dist/feedback-record-7WXGKXNS.js +0 -269
- package/dist/fetch-SLDDORH4.js +0 -179
- package/dist/fit-GNQHRKP7.js +0 -65
- package/dist/flows-NRPABVUJ.js +0 -250
- package/dist/fonts-VN2QXPAQ.js +0 -39
- package/dist/forecast-SIX6AAVX.js +0 -121
- package/dist/forecast-ZXOSLITM.js +0 -169
- package/dist/funnel-QEZWKSFR.js +0 -397
- package/dist/generate-HOMLLUCN.js +0 -263
- package/dist/get-BXMLPLIC.js +0 -82
- package/dist/get-NF3SPPVX.js +0 -59
- package/dist/google-XVSL3XAB.js +0 -3267
- package/dist/google-ads-5NBAMFPB.js +0 -198
- package/dist/gsc-72AFO6B4.js +0 -30
- package/dist/history-2U5OXDV7.js +0 -102
- package/dist/history-A6KMJRPZ.js +0 -69
- package/dist/history-IDVWUSYU.js +0 -136
- package/dist/hubspot-YPBNNVHC.js +0 -782
- package/dist/ideas-4Q7YBUGC.js +0 -45
- package/dist/images-BRBSRYEX.js +0 -92
- package/dist/ingest-AIOFOAQE.js +0 -27
- package/dist/ingest-HPEBFXCP.js +0 -123
- package/dist/insights-TR5EQAMA.js +0 -132
- package/dist/inspect-ITKB2QT3.js +0 -192
- package/dist/intent-E6HKRMNE.js +0 -95
- package/dist/keyword-gap-D4WAEJAH.js +0 -130
- package/dist/keyword-metrics-4UDT5GNW.js +0 -200
- package/dist/keywords-for-site-YQAQQIHJ.js +0 -131
- package/dist/landing-6JAIZH4H.js +0 -28
- package/dist/languages-AUAZIVL4.js +0 -40
- package/dist/leads-6XZRTPJL.js +0 -115
- package/dist/learn-VQGATNB4.js +0 -207
- package/dist/line-item-writes-TRTDQ55N.js +0 -211
- package/dist/line-items-SC73GQLQ.js +0 -107
- package/dist/link-ODLNZTZC.js +0 -67
- package/dist/linkedin-HPZP4MGS.js +0 -1328
- package/dist/linkedin-person-ELEPEST5.js +0 -139
- package/dist/list-26EOYW37.js +0 -154
- package/dist/list-GLN62MRB.js +0 -67
- package/dist/list-YLPOXOCI.js +0 -306
- package/dist/locations-D5N75BUK.js +0 -69
- package/dist/locations-P5PTCRSF.js +0 -40
- package/dist/log-66XLWNCD.js +0 -95
- package/dist/make-3ADCSNFC.js +0 -1367
- package/dist/map-3SGKGWSY.js +0 -436
- package/dist/mcp-ZSPJE3Y6.js +0 -729
- package/dist/media-sources-UBQCVRGK.js +0 -35
- package/dist/members-76XFDSMX.js +0 -47
- package/dist/metrics-XCD3IJSM.js +0 -152
- package/dist/models-WNK7Z6TR.js +0 -155
- package/dist/normalize-VC7HUODA.js +0 -144
- package/dist/options-5BPPM5N3.js +0 -125
- package/dist/organization-PXND43LQ.js +0 -949
- package/dist/performance-DDZOJ5GD.js +0 -5565
- package/dist/performance-Q25RDNM6.js +0 -27
- package/dist/pixels-KRK2B7M3.js +0 -62
- package/dist/promoted-tweet-writes-LIFJFF7N.js +0 -183
- package/dist/promoted-tweets-H5RMRQD2.js +0 -119
- package/dist/publish-LBHYEZW3.js +0 -344
- package/dist/query-A53T6QJF.js +0 -62
- package/dist/query-NDOYOIHL.js +0 -247
- package/dist/query-NDOYOIHL.js.map +0 -1
- package/dist/reddit-H4R23K27.js +0 -29
- package/dist/refresh-3QKUSERK.js +0 -97
- package/dist/release-JMYTQZLK.js +0 -63
- package/dist/relevant-pages-DOCKQEMZ.js +0 -99
- package/dist/remove-F7GEZMKB.js +0 -53
- package/dist/reopen-Y557X6IX.js +0 -66
- package/dist/rerun-RWBKQGVF.js +0 -96
- package/dist/research-3OJ5UZER.js +0 -265
- package/dist/research-AYO522XS.js +0 -72
- package/dist/reset-BL7M4LL7.js +0 -57
- package/dist/reset-PMVUQTYU.js +0 -57
- package/dist/resolve-CSWSGGK6.js +0 -124
- package/dist/retry-MVOPE2O2.js +0 -65
- package/dist/retry-WT46B5SC.js +0 -63
- package/dist/revenue-ZLE645Z5.js +0 -91
- package/dist/rules-Q6HWHFVU.js +0 -99
- package/dist/rules-add-7EPXWSQV.js +0 -465
- package/dist/run-4DE5XUVB.js +0 -71
- package/dist/run-PAND6N4V.js +0 -69
- package/dist/scaffold-ad-44INCR5V.js +0 -1513
- package/dist/scaffold-static-ad-ATXP44TE.js +0 -1011
- package/dist/scaffold-video-L4FQWQ5A.js +0 -885
- package/dist/schedule-CSE2XFFF.js +0 -64
- package/dist/scheduled-agents-NXHXQZBO.js +0 -55
- package/dist/schema-QITMY7ZE.js +0 -135
- package/dist/search-HOYOSX2H.js +0 -177
- package/dist/select-E5TP7JR6.js +0 -110
- package/dist/set-prompt-QGV3IUIP.js +0 -199
- package/dist/setup-links-HQ6WVKEP.js +0 -144
- package/dist/shopify-6MLRXSC2.js +0 -3958
- package/dist/site-5B44TV46.js +0 -180
- package/dist/sitemaps-6A6JFBV4.js +0 -87
- package/dist/sitemaps-6A6JFBV4.js.map +0 -1
- package/dist/sites-PZLE5FFV.js +0 -101
- package/dist/sites-PZLE5FFV.js.map +0 -1
- package/dist/sources-OMINR6RG.js +0 -30
- package/dist/sources-PNEWSEBG.js +0 -457
- package/dist/stale-G2BPCBYZ.js +0 -90
- package/dist/status-QNXQNAKH.js +0 -65
- package/dist/studio-BNYFU6ZH.js +0 -49
- package/dist/sync-OYOS7HQL.js +0 -57
- package/dist/sync-definition-2VEFGHYK.js +0 -70
- package/dist/tags-56H6O7ZL.js +0 -26
- package/dist/tags-CYR2IPEJ.js +0 -23
- package/dist/tags-RBUYBAA6.js +0 -227
- package/dist/tags-WVJNOATS.js +0 -23
- package/dist/tags-ZTVOZOYP.js +0 -23
- package/dist/targeting-criteria-EZNZOUAE.js +0 -113
- package/dist/targets-CU7IUPV2.js +0 -106
- package/dist/testimonials-THJJYQ42.js +0 -32
- package/dist/themes-TSEMQ4EJ.js +0 -95
- package/dist/top-companies-W6DA3MZQ.js +0 -111
- package/dist/transcript-KZ74PK23.js +0 -165
- package/dist/translate-FQOYDETZ.js +0 -603
- package/dist/triggers-OJKTDJSV.js +0 -144
- package/dist/unlink-ZE7464NK.js +0 -70
- package/dist/unstage-RGIPGM3E.js +0 -88
- package/dist/update-2VYVJCY2.js +0 -192
- package/dist/update-63MMKPHI.js +0 -155
- package/dist/update-JHWPEJN7.js +0 -66
- package/dist/update-WHHAH4IE.js +0 -101
- package/dist/upload-7XG5LU7C.js +0 -130
- package/dist/url-options-2BGF2D5E.js +0 -131
- package/dist/url-params-FBDTXQGY.js +0 -101
- package/dist/validate-Z3SRQKSO.js +0 -125
- package/dist/videos-4TLBI3E3.js +0 -41
- package/dist/viewer-check-ELVSS3DM.js +0 -83
- package/dist/web-LDR6HO5M.js +0 -143
- package/dist/winning-ads-7SWUVUDA.js +0 -86
- package/dist/write-commands-24FRMQW4.js +0 -1525
- package/dist/x-UOGRWXET.js +0 -47
- /package/dist/{account-BRRFMDXZ.js.map → account-2UXZ6CZJ.js.map} +0 -0
- /package/dist/{account-MQI7EZBH.js.map → account-OWMJG4IW.js.map} +0 -0
- /package/dist/{account-reads-K6YDAOHX.js.map → account-reads-W2GAN335.js.map} +0 -0
- /package/dist/{account-users-QS54ZEOD.js.map → account-users-WLTQNPL7.js.map} +0 -0
- /package/dist/{account-writes-PGGGP26M.js.map → account-writes-TNADUSHW.js.map} +0 -0
- /package/dist/{accounts-IW62WBX5.js.map → accounts-D3SQTZB7.js.map} +0 -0
- /package/dist/{accounts-H3U7LARM.js.map → accounts-HRQFOTBJ.js.map} +0 -0
- /package/dist/{actions-2CUQFZNA.js.map → actions-VJXED57L.js.map} +0 -0
- /package/dist/{activecampaign-WAMDVBUO.js.map → activecampaign-TNJIQA4C.js.map} +0 -0
- /package/dist/{ad-group-writes-ROUU6BHU.js.map → ad-group-writes-7BGCZA3M.js.map} +0 -0
- /package/dist/{ad-groups-Y2BSRLIN.js.map → ad-groups-A6GU6EW5.js.map} +0 -0
- /package/dist/{ad-writes-NF4LCGVP.js.map → ad-writes-CEMCUDU2.js.map} +0 -0
- /package/dist/{add-UHWRBSFE.js.map → add-DRTKSLEQ.js.map} +0 -0
- /package/dist/{add-4P3CUHAN.js.map → add-LBHISK44.js.map} +0 -0
- /package/dist/{ads-TDN7OYSU.js.map → ads-4EVKVAMR.js.map} +0 -0
- /package/dist/{ads-CPMX3L4A.js.map → ads-OY455VCX.js.map} +0 -0
- /package/dist/{advertisers-6CHV5N2V.js.map → advertisers-G6DZRBOK.js.map} +0 -0
- /package/dist/{analytics-4GTUW42V.js.map → analytics-ANG42PKM.js.map} +0 -0
- /package/dist/{analytics-MV2RQ6KR.js.map → analytics-UI4AKUSF.js.map} +0 -0
- /package/dist/{animate-EOSLSA7F.js.map → animate-KCSICY2N.js.map} +0 -0
- /package/dist/{assign-RN5DGDS2.js.map → assign-5DFH6HBV.js.map} +0 -0
- /package/dist/{audience-size-7DYMCXDM.js.map → audience-size-P6WIXOB3.js.map} +0 -0
- /package/dist/{audiences-VMGB2JZG.js.map → audiences-4ASLCNOF.js.map} +0 -0
- /package/dist/{audit-7U7G2JKS.js.map → audit-LA4BSLXV.js.map} +0 -0
- /package/dist/{autocomplete-E6YHJ3HH.js.map → autocomplete-XMG6E7TG.js.map} +0 -0
- /package/dist/{avatars-6JHVTSXH.js.map → avatars-4IFN4GZD.js.map} +0 -0
- /package/dist/{bid-pricing-J3OJK4J3.js.map → bid-pricing-JEOG2Q3L.js.map} +0 -0
- /package/dist/{brand-3EAQFDYN.js.map → brand-C6HUAK74.js.map} +0 -0
- /package/dist/{brief-ADGCGC3H.js.map → brief-5BCPPBU3.js.map} +0 -0
- /package/dist/{campaign-groups-GN7D5UXE.js.map → campaign-groups-IYQZSICZ.js.map} +0 -0
- /package/dist/{campaign-writes-UORRIIWY.js.map → campaign-writes-CVOOPZ35.js.map} +0 -0
- /package/dist/{campaign-writes-HKWI6GYU.js.map → campaign-writes-YXI5UOPH.js.map} +0 -0
- /package/dist/{campaigns-5I76AV7F.js.map → campaigns-FW4WVD25.js.map} +0 -0
- /package/dist/{campaigns-BEXDLBT3.js.map → campaigns-QMFHNG3N.js.map} +0 -0
- /package/dist/{campaigns-GONROKTU.js.map → campaigns-TX37WHVL.js.map} +0 -0
- /package/dist/{canvas-CHV34G62.js.map → canvas-U7MZXZKR.js.map} +0 -0
- /package/dist/{capabilities-VKBIQ2BR.js.map → capabilities-7643Y2HU.js.map} +0 -0
- /package/dist/{catalog-VXANSX2O.js.map → catalog-FLWVVX2U.js.map} +0 -0
- /package/dist/{catalogs-LXEM7NNC.js.map → catalogs-YNIUP2AH.js.map} +0 -0
- /package/dist/{changes-4OYC4NMW.js.map → changes-QUKBZVB3.js.map} +0 -0
- /package/dist/{chatgpt-XV6CVKLN.js.map → chatgpt-TIUBMATF.js.map} +0 -0
- /package/dist/{chats-W35ZPZEW.js.map → chats-BGLAG4Q7.js.map} +0 -0
- /package/dist/{check-3D63D4ZT.js.map → check-MN5ZQ7RX.js.map} +0 -0
- /package/dist/{chunk-OAXQPLTG.js.map → chunk-2UJ5ABI6.js.map} +0 -0
- /package/dist/{chunk-JGOYO7I2.js.map → chunk-2XRFWXSA.js.map} +0 -0
- /package/dist/{chunk-JUF6Q5X7.js.map → chunk-3OSBCYKA.js.map} +0 -0
- /package/dist/{chunk-5HGAO4ZI.js.map → chunk-3QQHPRRB.js.map} +0 -0
- /package/dist/{chunk-G3MEYGXC.js.map → chunk-3WTCMBDC.js.map} +0 -0
- /package/dist/{chunk-IZABTTW5.js.map → chunk-44ZRCSR7.js.map} +0 -0
- /package/dist/{chunk-WUCTGDV7.js.map → chunk-4QITYZUH.js.map} +0 -0
- /package/dist/{chunk-KUM36NSV.js.map → chunk-4XNF52NU.js.map} +0 -0
- /package/dist/{chunk-XCRYRVKK.js.map → chunk-4Y7IJ2PU.js.map} +0 -0
- /package/dist/{chunk-MHSVWQK6.js.map → chunk-53BTYIMJ.js.map} +0 -0
- /package/dist/{chunk-5YN53OAA.js.map → chunk-56OEYUJU.js.map} +0 -0
- /package/dist/{chunk-6CCEEVXJ.js.map → chunk-5N47LB7X.js.map} +0 -0
- /package/dist/{chunk-CBQPKCFE.js.map → chunk-5UBFYJHF.js.map} +0 -0
- /package/dist/{chunk-QUYDMAQN.js.map → chunk-6PMJU7IU.js.map} +0 -0
- /package/dist/{chunk-Z3Y67AH6.js.map → chunk-7I65KXU7.js.map} +0 -0
- /package/dist/{chunk-6QGBBX7F.js.map → chunk-BXUUKRMM.js.map} +0 -0
- /package/dist/{chunk-J3HA4RFY.js.map → chunk-BZTAEZLT.js.map} +0 -0
- /package/dist/{chunk-TOUF5ME6.js.map → chunk-CJQ77HO5.js.map} +0 -0
- /package/dist/{chunk-CWZ2ODJQ.js.map → chunk-CKTVRR3D.js.map} +0 -0
- /package/dist/{chunk-NP3FPCPN.js.map → chunk-COS3TPHX.js.map} +0 -0
- /package/dist/{chunk-ZKRRYVDG.js.map → chunk-CQ6HBFOB.js.map} +0 -0
- /package/dist/{chunk-6HGLQJ6A.js.map → chunk-DWW5QSUF.js.map} +0 -0
- /package/dist/{chunk-2UZHRLLO.js.map → chunk-ENUCJ6AO.js.map} +0 -0
- /package/dist/{chunk-5GHKZLID.js.map → chunk-ES3PWGS3.js.map} +0 -0
- /package/dist/{chunk-TGCDXZ5X.js.map → chunk-FJDSL4O5.js.map} +0 -0
- /package/dist/{chunk-GPZVYXJZ.js.map → chunk-FSTDWVXG.js.map} +0 -0
- /package/dist/{chunk-55B4AJY3.js.map → chunk-FUR7R4D7.js.map} +0 -0
- /package/dist/{chunk-OI2CKYAQ.js.map → chunk-HTT7RBRC.js.map} +0 -0
- /package/dist/{chunk-DYOUJERF.js.map → chunk-HVJJEQDQ.js.map} +0 -0
- /package/dist/{chunk-SOS4QFLE.js.map → chunk-I3LRC33M.js.map} +0 -0
- /package/dist/{chunk-45EERHDB.js.map → chunk-I7AYMJZA.js.map} +0 -0
- /package/dist/{chunk-G3MN4HIX.js.map → chunk-IU5K42Q3.js.map} +0 -0
- /package/dist/{chunk-6OIKNJ7G.js.map → chunk-JAC6JVCI.js.map} +0 -0
- /package/dist/{chunk-E3FMS5DA.js.map → chunk-JEUFRIYS.js.map} +0 -0
- /package/dist/{chunk-DGZYZWMK.js.map → chunk-KITVYLEX.js.map} +0 -0
- /package/dist/{chunk-7MY3QHGA.js.map → chunk-L5VVKIMB.js.map} +0 -0
- /package/dist/{chunk-UPXPBA4W.js.map → chunk-M2MJ52HS.js.map} +0 -0
- /package/dist/{chunk-BKOUVU6X.js.map → chunk-MBSAHNMJ.js.map} +0 -0
- /package/dist/{chunk-UUWUJ64R.js.map → chunk-MHZIOOT5.js.map} +0 -0
- /package/dist/{chunk-VYJLS64A.js.map → chunk-MIRPLAFD.js.map} +0 -0
- /package/dist/{chunk-G53CUQZV.js.map → chunk-MNOCC5AL.js.map} +0 -0
- /package/dist/{chunk-SIKCVWM3.js.map → chunk-MZPS46OR.js.map} +0 -0
- /package/dist/{chunk-AH6TDWYS.js.map → chunk-NZUMNTAT.js.map} +0 -0
- /package/dist/{chunk-2PC26XRF.js.map → chunk-OXLB5M5M.js.map} +0 -0
- /package/dist/{chunk-KAAYVKBH.js.map → chunk-OYNBUT5X.js.map} +0 -0
- /package/dist/{chunk-XPIBSDG4.js.map → chunk-PIAG4UJT.js.map} +0 -0
- /package/dist/{chunk-YBFZJ5EB.js.map → chunk-PV2JVXFM.js.map} +0 -0
- /package/dist/{chunk-XJ5TX772.js.map → chunk-QOBA6PHB.js.map} +0 -0
- /package/dist/{chunk-6RTZPBAT.js.map → chunk-R6WEPF63.js.map} +0 -0
- /package/dist/{chunk-DT3V5EGL.js.map → chunk-RQB4XLSF.js.map} +0 -0
- /package/dist/{chunk-Y6MLSN6W.js.map → chunk-RRC7F3QQ.js.map} +0 -0
- /package/dist/{chunk-MTV2RSGY.js.map → chunk-S6VFDNSQ.js.map} +0 -0
- /package/dist/{chunk-T65Y7N7H.js.map → chunk-SF52UGHZ.js.map} +0 -0
- /package/dist/{chunk-URMAEVZO.js.map → chunk-SG2TDQEE.js.map} +0 -0
- /package/dist/{chunk-YXQFCQX2.js.map → chunk-SWFZJROM.js.map} +0 -0
- /package/dist/{chunk-S556LOAP.js.map → chunk-T33M3PK7.js.map} +0 -0
- /package/dist/{chunk-T75WL56Z.js.map → chunk-TNKKK7XD.js.map} +0 -0
- /package/dist/{chunk-N4BK6OKX.js.map → chunk-TNOOWNN7.js.map} +0 -0
- /package/dist/{chunk-TWCS6FSC.js.map → chunk-UAB4CYBH.js.map} +0 -0
- /package/dist/{chunk-RIA5RXPL.js.map → chunk-UBDY5RE4.js.map} +0 -0
- /package/dist/{chunk-VGUGFRWF.js.map → chunk-UCNIJ2IW.js.map} +0 -0
- /package/dist/{chunk-DMA67YC3.js.map → chunk-UFUYTWEF.js.map} +0 -0
- /package/dist/{chunk-YR3OPPUE.js.map → chunk-UROKPFIS.js.map} +0 -0
- /package/dist/{chunk-56UWXQEN.js.map → chunk-VP4PO7FW.js.map} +0 -0
- /package/dist/{chunk-HGHVNUJT.js.map → chunk-WRTG3KXU.js.map} +0 -0
- /package/dist/{chunk-WEQZ563U.js.map → chunk-WWLZ5RRU.js.map} +0 -0
- /package/dist/{chunk-EIRGJD3Q.js.map → chunk-YE4RCEEJ.js.map} +0 -0
- /package/dist/{chunk-5G5QFFJY.js.map → chunk-YO54MA7R.js.map} +0 -0
- /package/dist/{claim-DWGPEVJN.js.map → claim-2JNNC66N.js.map} +0 -0
- /package/dist/{companies-3VT7OJAA.js.map → companies-4NJU5TLU.js.map} +0 -0
- /package/dist/{company-XEPW45AT.js.map → company-QOYWS4W6.js.map} +0 -0
- /package/dist/{complete-AEX34ICU.js.map → complete-7FX2K3PV.js.map} +0 -0
- /package/dist/{connect-TF4FCXDV.js.map → connect-H2HSPMRT.js.map} +0 -0
- /package/dist/{conversation-6WWZMPAB.js.map → conversation-4M7YMQOL.js.map} +0 -0
- /package/dist/{conversion-report-I6R2FDCD.js.map → conversion-report-G5JMZNYV.js.map} +0 -0
- /package/dist/{conversion-upload-command-QD6MGLY4.js.map → conversion-upload-command-43PZFYT7.js.map} +0 -0
- /package/dist/{conversion-writes-H2JOAORW.js.map → conversion-writes-JQQCCGPO.js.map} +0 -0
- /package/dist/{conversions-WGW43FUE.js.map → conversions-UO4CQNF6.js.map} +0 -0
- /package/dist/{conversions-D46PQRWF.js.map → conversions-Z7FW7CFT.js.map} +0 -0
- /package/dist/{create-7WV7XWZV.js.map → create-4SFO4AQZ.js.map} +0 -0
- /package/dist/{create-WSEYNFTP.js.map → create-D5MA64KL.js.map} +0 -0
- /package/dist/{create-OUZZLPQK.js.map → create-GJ4ZMY3A.js.map} +0 -0
- /package/dist/{create-QRT6O5PN.js.map → create-QVY6DSYM.js.map} +0 -0
- /package/dist/{creatives-DBDFDHX7.js.map → creatives-NTAKM5TG.js.map} +0 -0
- /package/dist/{creatives-BSZY7VDQ.js.map → creatives-R3XILT6D.js.map} +0 -0
- /package/dist/{credentials-P7GQYIE3.js.map → credentials-EQRB6XGZ.js.map} +0 -0
- /package/dist/{critique-X2YDOMON.js.map → critique-4YRRUNAU.js.map} +0 -0
- /package/dist/{critique-6F4FWQN5.js.map → critique-JCU2ATT6.js.map} +0 -0
- /package/dist/{crm-VNAUSVC5.js.map → crm-AF6XOOSF.js.map} +0 -0
- /package/dist/{crm-O6YG6777.js.map → crm-LSMFGR6R.js.map} +0 -0
- /package/dist/{currency-UVT3HEZT.js.map → currency-4KNOOZUU.js.map} +0 -0
- /package/dist/{customers-VJ3TXILQ.js.map → customers-MM3VIZNA.js.map} +0 -0
- /package/dist/{delete-3H377GOM.js.map → delete-CO2YBPTI.js.map} +0 -0
- /package/dist/{delete-EH3XL26Z.js.map → delete-D7HTW32U.js.map} +0 -0
- /package/dist/{delete-QYSOAVP4.js.map → delete-JUPBYK3H.js.map} +0 -0
- /package/dist/{demographics-GKJZ5PV2.js.map → demographics-DK5JDY6M.js.map} +0 -0
- /package/dist/{discard-46RALLBO.js.map → discard-DEJOM62Y.js.map} +0 -0
- /package/dist/{discover-C4TG7JP7.js.map → discover-U6YLVLTF.js.map} +0 -0
- /package/dist/{domain-JNNWJNQE.js.map → domain-MBOMV3CQ.js.map} +0 -0
- /package/dist/{download-3R5IHN5F.js.map → download-KN2I55SA.js.map} +0 -0
- /package/dist/{draft-3L4SNDGO.js.map → draft-BMNBT37B.js.map} +0 -0
- /package/dist/{draft-EADHX7XL.js.map → draft-CGPVRRKC.js.map} +0 -0
- /package/dist/{draft-XCYSZWA6.js.map → draft-F74OIT5O.js.map} +0 -0
- /package/dist/{draft-VSXBSXSX.js.map → draft-FIEZWBIX.js.map} +0 -0
- /package/dist/{draft-TAM7IUPH.js.map → draft-HZ5T6NOI.js.map} +0 -0
- /package/dist/{experiments-JVGAKWFA.js.map → experiments-YNJZCMQM.js.map} +0 -0
- /package/dist/{facets-JG44XXAT.js.map → facets-Y4OACBMC.js.map} +0 -0
- /package/dist/{feedback-3JCNU6MT.js.map → feedback-I5FZZAJR.js.map} +0 -0
- /package/dist/{feedback-record-7WXGKXNS.js.map → feedback-record-U2O24ITU.js.map} +0 -0
- /package/dist/{fetch-SLDDORH4.js.map → fetch-XU6QVWMS.js.map} +0 -0
- /package/dist/{fit-GNQHRKP7.js.map → fit-IHUUFCGI.js.map} +0 -0
- /package/dist/{flows-NRPABVUJ.js.map → flows-WSCIMXQX.js.map} +0 -0
- /package/dist/{fonts-VN2QXPAQ.js.map → fonts-3QW7EDHE.js.map} +0 -0
- /package/dist/{forecast-ZXOSLITM.js.map → forecast-2WVHMSZP.js.map} +0 -0
- /package/dist/{forecast-SIX6AAVX.js.map → forecast-F25LSCM5.js.map} +0 -0
- /package/dist/{funnel-QEZWKSFR.js.map → funnel-35DYDBJH.js.map} +0 -0
- /package/dist/{generate-HOMLLUCN.js.map → generate-Z7AROAES.js.map} +0 -0
- /package/dist/{get-NF3SPPVX.js.map → get-4CAX33L2.js.map} +0 -0
- /package/dist/{get-BXMLPLIC.js.map → get-N7X75ZJO.js.map} +0 -0
- /package/dist/{google-XVSL3XAB.js.map → google-M6XR7YGT.js.map} +0 -0
- /package/dist/{google-ads-5NBAMFPB.js.map → google-ads-NCSMNNND.js.map} +0 -0
- /package/dist/{gsc-72AFO6B4.js.map → gsc-GQFI6I2D.js.map} +0 -0
- /package/dist/{history-A6KMJRPZ.js.map → history-FGQ2H7XH.js.map} +0 -0
- /package/dist/{history-IDVWUSYU.js.map → history-LTOFKBMJ.js.map} +0 -0
- /package/dist/{history-2U5OXDV7.js.map → history-VJ254UXF.js.map} +0 -0
- /package/dist/{hubspot-YPBNNVHC.js.map → hubspot-W2NM75YK.js.map} +0 -0
- /package/dist/{ideas-4Q7YBUGC.js.map → ideas-54KFDNDM.js.map} +0 -0
- /package/dist/{images-BRBSRYEX.js.map → images-JOEVCCP6.js.map} +0 -0
- /package/dist/{ingest-AIOFOAQE.js.map → ingest-6R7XUL46.js.map} +0 -0
- /package/dist/{ingest-HPEBFXCP.js.map → ingest-VCIH5QDR.js.map} +0 -0
- /package/dist/{insights-TR5EQAMA.js.map → insights-UXUUOQB7.js.map} +0 -0
- /package/dist/{inspect-ITKB2QT3.js.map → inspect-MGOR77FP.js.map} +0 -0
- /package/dist/{intent-E6HKRMNE.js.map → intent-FZWPRUCI.js.map} +0 -0
- /package/dist/{keyword-gap-D4WAEJAH.js.map → keyword-gap-4ECYPWXJ.js.map} +0 -0
- /package/dist/{keyword-metrics-4UDT5GNW.js.map → keyword-metrics-SBIRN5KC.js.map} +0 -0
- /package/dist/{keywords-for-site-YQAQQIHJ.js.map → keywords-for-site-TLNPJHT2.js.map} +0 -0
- /package/dist/{landing-6JAIZH4H.js.map → landing-ZICSVWKF.js.map} +0 -0
- /package/dist/{languages-AUAZIVL4.js.map → languages-VNZQ5USL.js.map} +0 -0
- /package/dist/{leads-6XZRTPJL.js.map → leads-JUQFNK55.js.map} +0 -0
- /package/dist/{learn-VQGATNB4.js.map → learn-A6BT25LQ.js.map} +0 -0
- /package/dist/{line-item-writes-TRTDQ55N.js.map → line-item-writes-NW7A6HNH.js.map} +0 -0
- /package/dist/{line-items-SC73GQLQ.js.map → line-items-OAG3QMDE.js.map} +0 -0
- /package/dist/{link-ODLNZTZC.js.map → link-4VJ7FSOA.js.map} +0 -0
- /package/dist/{linkedin-HPZP4MGS.js.map → linkedin-CKOWKD4M.js.map} +0 -0
- /package/dist/{linkedin-person-ELEPEST5.js.map → linkedin-person-C22IDK7S.js.map} +0 -0
- /package/dist/{list-GLN62MRB.js.map → list-2DKLO6AO.js.map} +0 -0
- /package/dist/{list-YLPOXOCI.js.map → list-CDDXHMJQ.js.map} +0 -0
- /package/dist/{list-26EOYW37.js.map → list-ZORO7PFP.js.map} +0 -0
- /package/dist/{locations-D5N75BUK.js.map → locations-4RDUHKVR.js.map} +0 -0
- /package/dist/{locations-P5PTCRSF.js.map → locations-HATR6PSC.js.map} +0 -0
- /package/dist/{log-66XLWNCD.js.map → log-OSJHKHPV.js.map} +0 -0
- /package/dist/{make-3ADCSNFC.js.map → make-JPXGF44U.js.map} +0 -0
- /package/dist/{map-3SGKGWSY.js.map → map-PMGZDXAN.js.map} +0 -0
- /package/dist/{mcp-ZSPJE3Y6.js.map → mcp-MOOOTRYG.js.map} +0 -0
- /package/dist/{media-sources-UBQCVRGK.js.map → media-sources-5OYCKQ5J.js.map} +0 -0
- /package/dist/{members-76XFDSMX.js.map → members-QG5IP5HM.js.map} +0 -0
- /package/dist/{metrics-XCD3IJSM.js.map → metrics-R4YS2JL6.js.map} +0 -0
- /package/dist/{models-WNK7Z6TR.js.map → models-RJMZV5SH.js.map} +0 -0
- /package/dist/{normalize-VC7HUODA.js.map → normalize-LATTXSL3.js.map} +0 -0
- /package/dist/{options-5BPPM5N3.js.map → options-KALH5TB7.js.map} +0 -0
- /package/dist/{organization-PXND43LQ.js.map → organization-AGCF7DUJ.js.map} +0 -0
- /package/dist/{performance-DDZOJ5GD.js.map → performance-SITVZQWX.js.map} +0 -0
- /package/dist/{performance-Q25RDNM6.js.map → performance-YNXPUSGJ.js.map} +0 -0
- /package/dist/{pixels-KRK2B7M3.js.map → pixels-TYJVE377.js.map} +0 -0
- /package/dist/{promoted-tweet-writes-LIFJFF7N.js.map → promoted-tweet-writes-NAHPJ3VD.js.map} +0 -0
- /package/dist/{promoted-tweets-H5RMRQD2.js.map → promoted-tweets-2XQ4MWAC.js.map} +0 -0
- /package/dist/{publish-LBHYEZW3.js.map → publish-UITYPUBD.js.map} +0 -0
- /package/dist/{query-A53T6QJF.js.map → query-RE7NB2BK.js.map} +0 -0
- /package/dist/{reddit-H4R23K27.js.map → reddit-6XIARCTX.js.map} +0 -0
- /package/dist/{refresh-3QKUSERK.js.map → refresh-6UCN7PDX.js.map} +0 -0
- /package/dist/{release-JMYTQZLK.js.map → release-IN65L23O.js.map} +0 -0
- /package/dist/{relevant-pages-DOCKQEMZ.js.map → relevant-pages-W7IM6YQW.js.map} +0 -0
- /package/dist/{remove-F7GEZMKB.js.map → remove-X3XWVE2M.js.map} +0 -0
- /package/dist/{reopen-Y557X6IX.js.map → reopen-RMMQ55CH.js.map} +0 -0
- /package/dist/{rerun-RWBKQGVF.js.map → rerun-HCGLAUYW.js.map} +0 -0
- /package/dist/{research-AYO522XS.js.map → research-LZBPCXIW.js.map} +0 -0
- /package/dist/{research-3OJ5UZER.js.map → research-W5KUOBXZ.js.map} +0 -0
- /package/dist/{reset-BL7M4LL7.js.map → reset-MUWXTLIB.js.map} +0 -0
- /package/dist/{reset-PMVUQTYU.js.map → reset-VQNKXFAF.js.map} +0 -0
- /package/dist/{resolve-CSWSGGK6.js.map → resolve-DMGWLMTC.js.map} +0 -0
- /package/dist/{retry-WT46B5SC.js.map → retry-YI4YLYTQ.js.map} +0 -0
- /package/dist/{retry-MVOPE2O2.js.map → retry-ZQUJPKQH.js.map} +0 -0
- /package/dist/{revenue-ZLE645Z5.js.map → revenue-6YZAOMF6.js.map} +0 -0
- /package/dist/{rules-Q6HWHFVU.js.map → rules-FHVZEN4D.js.map} +0 -0
- /package/dist/{rules-add-7EPXWSQV.js.map → rules-add-7X46VTIJ.js.map} +0 -0
- /package/dist/{run-4DE5XUVB.js.map → run-2CSG6NIY.js.map} +0 -0
- /package/dist/{run-PAND6N4V.js.map → run-TA6HYFKZ.js.map} +0 -0
- /package/dist/{scaffold-ad-44INCR5V.js.map → scaffold-ad-AQK4PX7C.js.map} +0 -0
- /package/dist/{scaffold-static-ad-ATXP44TE.js.map → scaffold-static-ad-ORMMKUWY.js.map} +0 -0
- /package/dist/{scaffold-video-L4FQWQ5A.js.map → scaffold-video-525OYZYI.js.map} +0 -0
- /package/dist/{schedule-CSE2XFFF.js.map → schedule-TMTLYULK.js.map} +0 -0
- /package/dist/{scheduled-agents-NXHXQZBO.js.map → scheduled-agents-TS3WLRCM.js.map} +0 -0
- /package/dist/{schema-QITMY7ZE.js.map → schema-6S5ZEPBS.js.map} +0 -0
- /package/dist/{search-HOYOSX2H.js.map → search-LTUUAGZG.js.map} +0 -0
- /package/dist/{select-E5TP7JR6.js.map → select-XAC7G4ZT.js.map} +0 -0
- /package/dist/{set-prompt-QGV3IUIP.js.map → set-prompt-H3UEDJWS.js.map} +0 -0
- /package/dist/{setup-links-HQ6WVKEP.js.map → setup-links-6ANVKZCE.js.map} +0 -0
- /package/dist/{shopify-6MLRXSC2.js.map → shopify-6ISTKZCA.js.map} +0 -0
- /package/dist/{site-5B44TV46.js.map → site-OIV55MGO.js.map} +0 -0
- /package/dist/{sources-PNEWSEBG.js.map → sources-5L4S5BQV.js.map} +0 -0
- /package/dist/{sources-OMINR6RG.js.map → sources-GLJWZFHZ.js.map} +0 -0
- /package/dist/{stale-G2BPCBYZ.js.map → stale-TPLB2P4E.js.map} +0 -0
- /package/dist/{status-QNXQNAKH.js.map → status-TD7ISXC5.js.map} +0 -0
- /package/dist/{studio-BNYFU6ZH.js.map → studio-IDNM42JP.js.map} +0 -0
- /package/dist/{sync-OYOS7HQL.js.map → sync-PVIFUXX3.js.map} +0 -0
- /package/dist/{sync-definition-2VEFGHYK.js.map → sync-definition-K5TJORL5.js.map} +0 -0
- /package/dist/{tags-CYR2IPEJ.js.map → tags-3HB7N5WB.js.map} +0 -0
- /package/dist/{tags-RBUYBAA6.js.map → tags-DG3XQ4SF.js.map} +0 -0
- /package/dist/{tags-ZTVOZOYP.js.map → tags-T4EN6XYE.js.map} +0 -0
- /package/dist/{tags-56H6O7ZL.js.map → tags-T6THTVNP.js.map} +0 -0
- /package/dist/{tags-WVJNOATS.js.map → tags-XY6R2VJ6.js.map} +0 -0
- /package/dist/{targeting-criteria-EZNZOUAE.js.map → targeting-criteria-CL472KJO.js.map} +0 -0
- /package/dist/{targets-CU7IUPV2.js.map → targets-7AQMSO6H.js.map} +0 -0
- /package/dist/{testimonials-THJJYQ42.js.map → testimonials-4QWVSEPU.js.map} +0 -0
- /package/dist/{themes-TSEMQ4EJ.js.map → themes-ZTM4OVGK.js.map} +0 -0
- /package/dist/{top-companies-W6DA3MZQ.js.map → top-companies-PMFYOAFK.js.map} +0 -0
- /package/dist/{transcript-KZ74PK23.js.map → transcript-YWFYNFWX.js.map} +0 -0
- /package/dist/{translate-FQOYDETZ.js.map → translate-R2DXDXUY.js.map} +0 -0
- /package/dist/{triggers-OJKTDJSV.js.map → triggers-JKXISN45.js.map} +0 -0
- /package/dist/{unlink-ZE7464NK.js.map → unlink-IUAFDLBT.js.map} +0 -0
- /package/dist/{unstage-RGIPGM3E.js.map → unstage-ZYT5RNLK.js.map} +0 -0
- /package/dist/{update-63MMKPHI.js.map → update-23Q4T2J5.js.map} +0 -0
- /package/dist/{update-JHWPEJN7.js.map → update-74VME6PN.js.map} +0 -0
- /package/dist/{update-2VYVJCY2.js.map → update-O22DAZVX.js.map} +0 -0
- /package/dist/{update-WHHAH4IE.js.map → update-QAWK3O7L.js.map} +0 -0
- /package/dist/{upload-7XG5LU7C.js.map → upload-WASRCKS4.js.map} +0 -0
- /package/dist/{url-options-2BGF2D5E.js.map → url-options-M7RX2TD4.js.map} +0 -0
- /package/dist/{url-params-FBDTXQGY.js.map → url-params-E5ON6W2N.js.map} +0 -0
- /package/dist/{validate-Z3SRQKSO.js.map → validate-FGETFHYN.js.map} +0 -0
- /package/dist/{videos-4TLBI3E3.js.map → videos-SS2Y6FJF.js.map} +0 -0
- /package/dist/{viewer-check-ELVSS3DM.js.map → viewer-check-CWS6OVM7.js.map} +0 -0
- /package/dist/{web-LDR6HO5M.js.map → web-HT22JFQR.js.map} +0 -0
- /package/dist/{winning-ads-7SWUVUDA.js.map → winning-ads-FXTP4PWZ.js.map} +0 -0
- /package/dist/{write-commands-24FRMQW4.js.map → write-commands-5YVQKO7E.js.map} +0 -0
- /package/dist/{x-UOGRWXET.js.map → x-KEMAY75C.js.map} +0 -0
package/dist/chunk-AH6TDWYS.js
DELETED
|
@@ -1,3510 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
LOGO_POSITIONS,
|
|
3
|
-
MIN_LEGIBLE_SHARE,
|
|
4
|
-
PLACEMENT_DEFECTS,
|
|
5
|
-
PLACEMENT_EDGES,
|
|
6
|
-
PLACEMENT_ELEMENTS,
|
|
7
|
-
chooseLogoSpot,
|
|
8
|
-
contrastRatio,
|
|
9
|
-
deliveredSize,
|
|
10
|
-
designerSpots,
|
|
11
|
-
fitArtwork,
|
|
12
|
-
greyTable,
|
|
13
|
-
hexLuma,
|
|
14
|
-
inflatedWords,
|
|
15
|
-
inkLuma,
|
|
16
|
-
legibleShare,
|
|
17
|
-
logoArtwork,
|
|
18
|
-
logoBoxOn,
|
|
19
|
-
logoSlotLine,
|
|
20
|
-
logoSpotClean,
|
|
21
|
-
pickLogoVariant,
|
|
22
|
-
placeInSlot,
|
|
23
|
-
placementProblems,
|
|
24
|
-
plateColour,
|
|
25
|
-
slotAround,
|
|
26
|
-
spreadUnder,
|
|
27
|
-
straightEdgeIn,
|
|
28
|
-
textColourSource,
|
|
29
|
-
usesTextColour
|
|
30
|
-
} from "./chunk-XCRYRVKK.js";
|
|
31
|
-
import {
|
|
32
|
-
FRAME_CUT,
|
|
33
|
-
bandViolations,
|
|
34
|
-
blockingArea,
|
|
35
|
-
blockingPlacements,
|
|
36
|
-
boxesOfLine,
|
|
37
|
-
buttonShape,
|
|
38
|
-
bytesOf,
|
|
39
|
-
headlineBox,
|
|
40
|
-
isButtonLine,
|
|
41
|
-
readTextBoxes,
|
|
42
|
-
roleKey,
|
|
43
|
-
shownBoxes,
|
|
44
|
-
withWordmark
|
|
45
|
-
} from "./chunk-G3MEYGXC.js";
|
|
46
|
-
import {
|
|
47
|
-
askAboutImages
|
|
48
|
-
} from "./chunk-DGZYZWMK.js";
|
|
49
|
-
import {
|
|
50
|
-
IMAGE_GENERATE_MODELS,
|
|
51
|
-
IMAGE_TIER_CREDITS,
|
|
52
|
-
MODEL_REGISTRY,
|
|
53
|
-
SIZE_MATCH_MODEL,
|
|
54
|
-
SUBJECT_BOX_READER_MODEL,
|
|
55
|
-
imagePictureCredits,
|
|
56
|
-
modelIdsAccepted,
|
|
57
|
-
supportedAspectRatios
|
|
58
|
-
} from "./chunk-F3CZFUXZ.js";
|
|
59
|
-
import {
|
|
60
|
-
ImageRef,
|
|
61
|
-
callBackendExec,
|
|
62
|
-
defineNode,
|
|
63
|
-
delegated
|
|
64
|
-
} from "./chunk-SVNH6P7O.js";
|
|
65
|
-
|
|
66
|
-
// src/engine/nodes/remote/image.ts
|
|
67
|
-
import { z as z2 } from "zod";
|
|
68
|
-
|
|
69
|
-
// src/engine/nodes/remote/registry-params.ts
|
|
70
|
-
import { z } from "zod";
|
|
71
|
-
function registryParamShape(kind, overrides = {}) {
|
|
72
|
-
const specs = Object.values(MODEL_REGISTRY[kind]);
|
|
73
|
-
const byParam = /* @__PURE__ */ new Map();
|
|
74
|
-
for (const spec of specs) {
|
|
75
|
-
for (const [name, schema] of Object.entries(spec.params)) {
|
|
76
|
-
byParam.set(name, [...byParam.get(name) ?? [], schema]);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
const shape = {};
|
|
80
|
-
for (const [name, schemas] of byParam) {
|
|
81
|
-
const type = overrides[name] ?? unionOf(schemas);
|
|
82
|
-
const requiredByAll = specs.every((spec) => spec.required.includes(name));
|
|
83
|
-
shape[name] = requiredByAll ? type : type.optional();
|
|
84
|
-
}
|
|
85
|
-
return shape;
|
|
86
|
-
}
|
|
87
|
-
function unionOf(schemas) {
|
|
88
|
-
const kinds = [...new Set(schemas.map((schema) => schema.kind))];
|
|
89
|
-
if (kinds.length > 1) {
|
|
90
|
-
const [first, second, ...rest] = kinds.map((k) => unionOf(schemas.filter((s) => s.kind === k)));
|
|
91
|
-
return z.union([first, second, ...rest]);
|
|
92
|
-
}
|
|
93
|
-
switch (kinds[0]) {
|
|
94
|
-
case "string":
|
|
95
|
-
return stringUnion(schemas);
|
|
96
|
-
case "number":
|
|
97
|
-
return numberUnion(schemas);
|
|
98
|
-
case "boolean":
|
|
99
|
-
return z.boolean();
|
|
100
|
-
default:
|
|
101
|
-
return z.unknown();
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
function stringUnion(schemas) {
|
|
105
|
-
if (schemas.some((schema) => !schema.enum)) return z.string();
|
|
106
|
-
const values = [...new Set(schemas.flatMap((schema) => schema.enum ?? []))];
|
|
107
|
-
return z.enum(values);
|
|
108
|
-
}
|
|
109
|
-
function numberUnion(schemas) {
|
|
110
|
-
if (schemas.some((schema) => !schema.enum)) return z.number();
|
|
111
|
-
const values = [...new Set(schemas.flatMap((schema) => schema.enum ?? []))].sort((a, b) => a - b);
|
|
112
|
-
return z.literal(values);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// src/engine/nodes/remote/image.ts
|
|
116
|
-
var imageModelEnum = z2.enum(IMAGE_GENERATE_MODELS.flatMap(modelIdsAccepted));
|
|
117
|
-
var ImageGenerateParams = z2.object({
|
|
118
|
-
model: imageModelEnum,
|
|
119
|
-
...registryParamShape("image_generate", {
|
|
120
|
-
prompt: z2.string().min(1),
|
|
121
|
-
// Recraft v4 vector controls, sent as the provider's own `controls` block. The
|
|
122
|
-
// registry types them as JSON; this is their shape.
|
|
123
|
-
rgb_colors: z2.array(z2.array(z2.number().int().min(0).max(255))),
|
|
124
|
-
background_rgb_color: z2.array(z2.number().int().min(0).max(255))
|
|
125
|
-
})
|
|
126
|
-
}).strict();
|
|
127
|
-
var PRO_QUALITY = "max";
|
|
128
|
-
function creditsPerImage(params) {
|
|
129
|
-
return params.quality === PRO_QUALITY ? IMAGE_TIER_CREDITS.pro : imagePictureCredits(String(params.model));
|
|
130
|
-
}
|
|
131
|
-
function imagesPerCall(params) {
|
|
132
|
-
return "n" in params && typeof params.n === "number" ? params.n : 1;
|
|
133
|
-
}
|
|
134
|
-
var imageGenerateNode = delegated({
|
|
135
|
-
id: "image_generate",
|
|
136
|
-
version: "2.2.0",
|
|
137
|
-
category: "image",
|
|
138
|
-
summary: "Generate images for ad creatives. Curated model set: GPT Image 2.5, Gemini 3.1 Flash Image, Gemini 3 Pro Image, Recraft v4.1 Pro Vector. Per-model param support comes from the canvas-engine model registry.",
|
|
139
|
-
when_to_use: "Use for hero shots, product photography, illustrations, and vector logos. `recraft/recraft-v4.1-pro-vector` for crisp vector / logo work; `openai/gpt-image-2` for photorealistic and in-image text (NOTE: it has no `image_size`, and no 4:5 / 5:4 \u2014 route portrait ad formats through Gemini or `image_aspect_adapt`); Gemini variants for fast iteration, extreme ratios, and editing via the `reference` input. `reference` accepts ONE image or an ARRAY of images \u2014 wire several to combine references in a single generation (e.g. a subject sheet + a font specimen + the original ad). Every reference is forwarded to the model in array order. `n` (only on models that declare it, e.g. `openai/gpt-image-2.5-sunburst`) returns several candidates of the same brief from one call, billed per image like separate calls; wire the whole `images` array into `image_select` to keep the best.",
|
|
140
|
-
// `reference` is one image or an ordered array of images. The backend forwards
|
|
141
|
-
// each as a separate `image_url` to the provider (OpenRouter accepts many).
|
|
142
|
-
inputs: z2.object({ reference: z2.union([ImageRef, z2.array(ImageRef).min(1)]).optional() }).loose(),
|
|
143
|
-
params: ImageGenerateParams,
|
|
144
|
-
outputs: z2.object({ images: z2.array(ImageRef).min(1) }).strict(),
|
|
145
|
-
outputKinds: { images: "image" },
|
|
146
|
-
// Per image: `n` asks one call for several, and the provider bills each (measured: n=3
|
|
147
|
-
// cost exactly 3x n=1, prompt tokens included). Usage-billed, so this is only the pre-run
|
|
148
|
-
// quote; the charge is the provider's reported cost.
|
|
149
|
-
cost: ({ params }) => ({
|
|
150
|
-
credits: creditsPerImage(params) * imagesPerCall(params),
|
|
151
|
-
seconds_estimate: 10
|
|
152
|
-
})
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
// src/engine/nodes/local/staticFormat.ts
|
|
156
|
-
import sharp4 from "sharp";
|
|
157
|
-
import { z as z4 } from "zod";
|
|
158
|
-
|
|
159
|
-
// src/engine/nodes/local/lib/node-steps.ts
|
|
160
|
-
import sharp2 from "sharp";
|
|
161
|
-
|
|
162
|
-
// src/engine/nodes/local/lib/subject-boxes.ts
|
|
163
|
-
import sharp from "sharp";
|
|
164
|
-
import { z as z3 } from "zod";
|
|
165
|
-
var SUBJECT_KINDS = ["person", "face", "animal", "product"];
|
|
166
|
-
var SubjectBoxSchema = z3.object({
|
|
167
|
-
kind: z3.enum(SUBJECT_KINDS),
|
|
168
|
-
label: z3.string(),
|
|
169
|
-
x0: z3.number().min(0).max(1),
|
|
170
|
-
y0: z3.number().min(0).max(1),
|
|
171
|
-
x1: z3.number().min(0).max(1),
|
|
172
|
-
y1: z3.number().min(0).max(1)
|
|
173
|
-
}).strict();
|
|
174
|
-
var GRID = 1e3;
|
|
175
|
-
var ReaderAnswer = z3.object({
|
|
176
|
-
items: z3.array(z3.object({ kind: z3.enum(SUBJECT_KINDS), label: z3.string(), box_2d: z3.array(z3.number()).length(4) }))
|
|
177
|
-
});
|
|
178
|
-
var fraction = (value) => Math.min(1, Math.max(0, value / GRID));
|
|
179
|
-
function subjectBoxesFromReader(answer) {
|
|
180
|
-
const parsed = ReaderAnswer.safeParse(answer);
|
|
181
|
-
if (!parsed.success) return null;
|
|
182
|
-
return parsed.data.items.map(({ kind, label, box_2d }) => {
|
|
183
|
-
const [ymin = 0, xmin = 0, ymax = 0, xmax = 0] = box_2d;
|
|
184
|
-
return {
|
|
185
|
-
kind,
|
|
186
|
-
label,
|
|
187
|
-
x0: fraction(Math.min(xmin, xmax)),
|
|
188
|
-
y0: fraction(Math.min(ymin, ymax)),
|
|
189
|
-
x1: fraction(Math.max(xmin, xmax)),
|
|
190
|
-
y1: fraction(Math.max(ymin, ymax))
|
|
191
|
-
};
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
var SUBJECT_BOX_PROMPT = [
|
|
195
|
-
"Find every person, every face, every animal and every product or object the ad is selling in this image.",
|
|
196
|
-
"For each, return: `kind` person, face, animal or product; `label` two or three words naming it; `box_2d` the tight box around all of it as [ymin, xmin, ymax, xmax] on a 0-1000 grid over the whole image.",
|
|
197
|
-
"A person is the whole body that is visible, a face is the head from the hair to the chin; a person and their face are two entries. A product is the thing sold or shown in use (a phone, a box, a dish), not furniture or background. Text and logos are not subjects. Nothing of the kind: an empty list.",
|
|
198
|
-
'Reply with only this JSON: {"items":[{"kind":"person|face|animal|product","label":"...","box_2d":[ymin,xmin,ymax,xmax]}]}'
|
|
199
|
-
].join("\n");
|
|
200
|
-
var READ_EDGE = 1536;
|
|
201
|
-
async function readSubjectBoxes(image, opts = {}) {
|
|
202
|
-
let jpeg;
|
|
203
|
-
try {
|
|
204
|
-
const resized = await sharp(image).resize(READ_EDGE, READ_EDGE, { fit: "inside", withoutEnlargement: true }).jpeg({ quality: 90 }).toBuffer();
|
|
205
|
-
jpeg = resized.toString("base64");
|
|
206
|
-
} catch {
|
|
207
|
-
return { status: "not_checked" };
|
|
208
|
-
}
|
|
209
|
-
for (let attempt = 0; attempt < 2; attempt++) {
|
|
210
|
-
const answer = await askAboutImages(SUBJECT_BOX_READER_MODEL, [jpeg], SUBJECT_BOX_PROMPT, opts.onSpend, 6e4, {
|
|
211
|
-
effort: "minimal"
|
|
212
|
-
});
|
|
213
|
-
const boxes = subjectBoxesFromReader(answer);
|
|
214
|
-
if (boxes) return { status: "read", boxes };
|
|
215
|
-
}
|
|
216
|
-
return { status: "not_checked" };
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// src/engine/nodes/local/lib/node-steps.ts
|
|
220
|
-
async function timedStep(run, name, key, fn, opts = {}) {
|
|
221
|
-
const t0 = Date.now();
|
|
222
|
-
let credits = 0;
|
|
223
|
-
const stepCtx = {
|
|
224
|
-
...run.ctx,
|
|
225
|
-
reportCharge: (c) => {
|
|
226
|
-
credits += c;
|
|
227
|
-
run.ctx.reportCharge?.(c);
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
|
-
const go = (suffix) => fn(suffix, stepCtx);
|
|
231
|
-
const { value, cached } = run.ctx.steps ? await run.ctx.steps.run(name, key, go, opts) : { value: await go(name), cached: false };
|
|
232
|
-
run.steps.push({ name, cached, credits, ms: Date.now() - t0 });
|
|
233
|
-
return value;
|
|
234
|
-
}
|
|
235
|
-
var isRead = (reading) => reading.status === "read";
|
|
236
|
-
async function subjectsStep(run, image, bytes) {
|
|
237
|
-
const read = await timedStep(
|
|
238
|
-
run,
|
|
239
|
-
"subjects",
|
|
240
|
-
{ image: image.sha256 },
|
|
241
|
-
() => readSubjectBoxes(bytes, { onSpend: (u) => run.usage.push(u) }),
|
|
242
|
-
{ keep: isRead }
|
|
243
|
-
);
|
|
244
|
-
return read.status === "read" ? read.boxes : null;
|
|
245
|
-
}
|
|
246
|
-
async function imageOf(ref, node) {
|
|
247
|
-
const at = ref.path ?? ref.url;
|
|
248
|
-
if (!at) throw new Error(`${node}: a picture it was given has no file`);
|
|
249
|
-
const bytes = await bytesOf(at);
|
|
250
|
-
const meta = await sharp2(bytes).metadata();
|
|
251
|
-
return { bytes, size: { width: meta.width ?? 0, height: meta.height ?? 0 } };
|
|
252
|
-
}
|
|
253
|
-
async function rawOf(bytes, size) {
|
|
254
|
-
const { data, info } = await sharp2(bytes).removeAlpha().resize(size.width, size.height, { fit: "fill", kernel: "lanczos3" }).raw().toBuffer({ resolveWithObject: true });
|
|
255
|
-
return { data, width: size.width, height: size.height, channels: info.channels };
|
|
256
|
-
}
|
|
257
|
-
function pngOf(raw) {
|
|
258
|
-
return sharp2(raw.data, { raw: { width: raw.width, height: raw.height, channels: raw.channels } }).png().toBuffer();
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// src/engine/nodes/local/lib/plate-panels.ts
|
|
262
|
-
var READ_WIDTH = 288;
|
|
263
|
-
var FLAT = 2;
|
|
264
|
-
var STEP = 3;
|
|
265
|
-
var RANGE = 12;
|
|
266
|
-
var MIN_AREA = 0.015;
|
|
267
|
-
var MIN_FILL = 0.6;
|
|
268
|
-
function blockMean(img, x, y, f, c) {
|
|
269
|
-
let sum = 0;
|
|
270
|
-
for (let dy = 0; dy < f; dy++) {
|
|
271
|
-
for (let dx = 0; dx < f; dx++) sum += img.data[((y + dy) * img.width + x + dx) * img.channels + c] ?? 0;
|
|
272
|
-
}
|
|
273
|
-
return sum / (f * f);
|
|
274
|
-
}
|
|
275
|
-
function shrunk(img, f) {
|
|
276
|
-
const width = Math.floor(img.width / f);
|
|
277
|
-
const height = Math.floor(img.height / f);
|
|
278
|
-
const data = Buffer.alloc(width * height * 3);
|
|
279
|
-
for (let i = 0; i < width * height; i++) {
|
|
280
|
-
const x = i % width;
|
|
281
|
-
const y = (i - x) / width;
|
|
282
|
-
for (let c = 0; c < 3; c++) data[i * 3 + c] = Math.round(blockMean(img, x * f, y * f, f, c));
|
|
283
|
-
}
|
|
284
|
-
return { data, width, height, channels: 3 };
|
|
285
|
-
}
|
|
286
|
-
function spreadAt(grey, width, x, y) {
|
|
287
|
-
let sum = 0;
|
|
288
|
-
let squares = 0;
|
|
289
|
-
for (let dy = -2; dy <= 2; dy++) {
|
|
290
|
-
for (let dx = -2; dx <= 2; dx++) {
|
|
291
|
-
const v = grey[(y + dy) * width + x + dx] ?? 0;
|
|
292
|
-
sum += v;
|
|
293
|
-
squares += v * v;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
const mean = sum / 25;
|
|
297
|
-
return Math.sqrt(Math.max(0, squares / 25 - mean * mean));
|
|
298
|
-
}
|
|
299
|
-
function flatOf(img) {
|
|
300
|
-
const { width, height } = img;
|
|
301
|
-
const grey = new Float32Array(width * height);
|
|
302
|
-
for (let i = 0; i < grey.length; i++)
|
|
303
|
-
grey[i] = 0.299 * (img.data[i * 3] ?? 0) + 0.587 * (img.data[i * 3 + 1] ?? 0) + 0.114 * (img.data[i * 3 + 2] ?? 0);
|
|
304
|
-
const flat = new Uint8Array(width * height);
|
|
305
|
-
for (let y = 2; y < height - 2; y++) {
|
|
306
|
-
for (let x = 2; x < width - 2; x++) flat[y * width + x] = spreadAt(grey, width, x, y) < FLAT ? 1 : 0;
|
|
307
|
-
}
|
|
308
|
-
return flat;
|
|
309
|
-
}
|
|
310
|
-
function take(r, img, i, x, y) {
|
|
311
|
-
r.count++;
|
|
312
|
-
r.x0 = Math.min(r.x0, x);
|
|
313
|
-
r.x1 = Math.max(r.x1, x);
|
|
314
|
-
r.y0 = Math.min(r.y0, y);
|
|
315
|
-
r.y1 = Math.max(r.y1, y);
|
|
316
|
-
for (let c = 0; c < 3; c++) {
|
|
317
|
-
const v = img.data[i * 3 + c] ?? 0;
|
|
318
|
-
r.lo[c] = Math.min(r.lo[c] ?? 255, v);
|
|
319
|
-
r.hi[c] = Math.max(r.hi[c] ?? 0, v);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
var neighbours = (i, x, width, height) => [x > 0 ? i - 1 : -1, x < width - 1 ? i + 1 : -1, i - width, i + width].filter((j) => j >= 0 && j < width * height);
|
|
323
|
-
function regionFrom(img, flat, seen, seed) {
|
|
324
|
-
const { width, height } = img;
|
|
325
|
-
const r = { x0: width, y0: height, x1: -1, y1: -1, count: 0, range: 0, lo: [255, 255, 255], hi: [0, 0, 0] };
|
|
326
|
-
const near = (a, b) => [0, 1, 2].every((c) => Math.abs((img.data[a * 3 + c] ?? 0) - (img.data[b * 3 + c] ?? 0)) <= STEP);
|
|
327
|
-
seen[seed] = 1;
|
|
328
|
-
const stack = [seed];
|
|
329
|
-
for (let i = stack.pop(); i !== void 0; i = stack.pop()) {
|
|
330
|
-
const x = i % width;
|
|
331
|
-
take(r, img, i, x, (i - x) / width);
|
|
332
|
-
for (const j of neighbours(i, x, width, height)) {
|
|
333
|
-
if (!flat[j] || seen[j] || !near(i, j)) continue;
|
|
334
|
-
seen[j] = 1;
|
|
335
|
-
stack.push(j);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
const range = Math.max(...[0, 1, 2].map((c) => (r.hi[c] ?? 0) - (r.lo[c] ?? 0)));
|
|
339
|
-
return { x0: r.x0, y0: r.y0, x1: r.x1, y1: r.y1, count: r.count, range };
|
|
340
|
-
}
|
|
341
|
-
function isPanel(r, width, height) {
|
|
342
|
-
const inside = r.x0 > 2 && r.y0 > 2 && r.x1 < width - 3 && r.y1 < height - 3;
|
|
343
|
-
const box = (r.x1 - r.x0 + 1) * (r.y1 - r.y0 + 1);
|
|
344
|
-
return inside && box >= MIN_AREA * width * height && r.count >= MIN_FILL * box && r.range <= RANGE;
|
|
345
|
-
}
|
|
346
|
-
function platePanels(img) {
|
|
347
|
-
const f = Math.max(1, Math.round(img.width / READ_WIDTH));
|
|
348
|
-
const small = f > 1 || img.channels !== 3 ? shrunk(img, f) : img;
|
|
349
|
-
const flat = flatOf(small);
|
|
350
|
-
const seen = new Uint8Array(flat.length);
|
|
351
|
-
const panels = [];
|
|
352
|
-
for (let i = 0; i < flat.length; i++) {
|
|
353
|
-
if (!flat[i] || seen[i]) continue;
|
|
354
|
-
const r = regionFrom(small, flat, seen, i);
|
|
355
|
-
if (!isPanel(r, small.width, small.height)) continue;
|
|
356
|
-
panels.push({
|
|
357
|
-
x0: r.x0 / small.width,
|
|
358
|
-
y0: r.y0 / small.height,
|
|
359
|
-
x1: (r.x1 + 1) / small.width,
|
|
360
|
-
y1: (r.y1 + 1) / small.height,
|
|
361
|
-
area: (r.x1 - r.x0 + 1) * (r.y1 - r.y0 + 1)
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
return panels.sort((a, b) => b.area - a.area).map(({ area: _area, ...span }) => span);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// src/engine/nodes/local/lib/subject-crop.ts
|
|
368
|
-
var SUBJECT_PAD = 0.02;
|
|
369
|
-
function ratioOf(ratio) {
|
|
370
|
-
const [w, h] = ratio.split(":").map(Number);
|
|
371
|
-
if (!w || !h || !Number.isFinite(w) || !Number.isFinite(h)) throw new Error(`not a W:H ratio: "${ratio}"`);
|
|
372
|
-
return w / h;
|
|
373
|
-
}
|
|
374
|
-
var unionOf2 = (spans) => spans.length === 0 ? null : { from: Math.min(...spans.map((s) => s.from)), to: Math.max(...spans.map((s) => s.to)) };
|
|
375
|
-
function placeAxis(full, length, hard, soft, core) {
|
|
376
|
-
const clampFull = (v) => Math.min(full - length, Math.max(0, v));
|
|
377
|
-
const want = soft ? soft.to - soft.from <= length ? (soft.from + soft.to) / 2 - length / 2 : soft.from - SUBJECT_PAD * full : (core.from + core.to) / 2 - length / 2;
|
|
378
|
-
if (!hard) return { at: Math.round(clampFull(want)), fits: true };
|
|
379
|
-
const pad = SUBJECT_PAD * full;
|
|
380
|
-
for (const p of [pad, 0]) {
|
|
381
|
-
const lo = Math.max(0, hard.to + p - length);
|
|
382
|
-
const hi = Math.min(full - length, hard.from - p);
|
|
383
|
-
if (lo <= hi + 1e-6) return { at: Math.round(Math.min(hi, Math.max(lo, want))), fits: true };
|
|
384
|
-
}
|
|
385
|
-
return { at: Math.round(clampFull((hard.from + hard.to) / 2 - length / 2)), fits: false };
|
|
386
|
-
}
|
|
387
|
-
function subjectCrop(plate, format, subjects, opts = {}) {
|
|
388
|
-
const cards = (opts.panels ?? []).map((p) => ({ kind: "product", label: "card", ...p }));
|
|
389
|
-
if (cards.length > 0) {
|
|
390
|
-
const withCards = cropAround(plate, format, [...subjects ?? [], ...cards], opts);
|
|
391
|
-
if (withCards.fits) return withCards;
|
|
392
|
-
}
|
|
393
|
-
return cropAround(plate, format, subjects, opts);
|
|
394
|
-
}
|
|
395
|
-
function cropAround(plate, format, subjects, opts) {
|
|
396
|
-
const r = ratioOf(format);
|
|
397
|
-
let width = plate.width;
|
|
398
|
-
let height = Math.round(plate.width / r);
|
|
399
|
-
if (height > plate.height) {
|
|
400
|
-
height = plate.height;
|
|
401
|
-
width = Math.round(plate.height * r);
|
|
402
|
-
}
|
|
403
|
-
const boxes = subjects ?? [];
|
|
404
|
-
const px = (b) => ({ from: b.x0 * plate.width, to: b.x1 * plate.width });
|
|
405
|
-
const py = (b) => ({ from: b.y0 * plate.height, to: b.y1 * plate.height });
|
|
406
|
-
const hardY = boxes.map((b) => b.kind === "person" ? { from: py(b).from, to: py(b).from } : py(b));
|
|
407
|
-
const core = opts.core ?? { x0: 0, y0: 0, x1: 1, y1: 1 };
|
|
408
|
-
const x = placeAxis(plate.width, width, unionOf2(boxes.map(px)), unionOf2(boxes.map(px)), {
|
|
409
|
-
from: core.x0 * plate.width,
|
|
410
|
-
to: core.x1 * plate.width
|
|
411
|
-
});
|
|
412
|
-
const y = placeAxis(plate.height, height, unionOf2(hardY), unionOf2(boxes.map(py)), {
|
|
413
|
-
from: core.y0 * plate.height,
|
|
414
|
-
to: core.y1 * plate.height
|
|
415
|
-
});
|
|
416
|
-
const rect = { x: x.at, y: y.at, width, height };
|
|
417
|
-
const cut = boxes.filter((b, i) => {
|
|
418
|
-
const hy = hardY[i] ?? py(b);
|
|
419
|
-
return px(b).from < rect.x - 0.5 || px(b).to > rect.x + rect.width + 0.5 || hy.from < rect.y - 0.5 || hy.to > rect.y + rect.height + 0.5;
|
|
420
|
-
}).map((b) => b.label);
|
|
421
|
-
return { rect, render: renderRect(plate, rect, format, opts.renderRatios), fits: cut.length === 0, cut };
|
|
422
|
-
}
|
|
423
|
-
function renderRect(plate, rect, format, ratios) {
|
|
424
|
-
if (!ratios || ratios.includes(format)) return { ...rect, ratio: format };
|
|
425
|
-
const r = rect.width / rect.height;
|
|
426
|
-
let best = null;
|
|
427
|
-
for (const ratio of ratios) {
|
|
428
|
-
const c = ratioOf(ratio);
|
|
429
|
-
const width = c <= r ? rect.width : Math.round(rect.height * c);
|
|
430
|
-
const height = c <= r ? Math.round(rect.width / c) : rect.height;
|
|
431
|
-
if (width > plate.width || height > plate.height) continue;
|
|
432
|
-
const place = (from, length, inner, full) => Math.min(
|
|
433
|
-
Math.min(from, full - length),
|
|
434
|
-
Math.max(Math.max(0, from + inner - length), from - (length - inner) / 2)
|
|
435
|
-
);
|
|
436
|
-
const candidate = {
|
|
437
|
-
x: Math.round(place(rect.x, width, rect.width, plate.width)),
|
|
438
|
-
y: Math.round(place(rect.y, height, rect.height, plate.height)),
|
|
439
|
-
width,
|
|
440
|
-
height,
|
|
441
|
-
ratio
|
|
442
|
-
};
|
|
443
|
-
if (!best || width * height < best.width * best.height) best = candidate;
|
|
444
|
-
}
|
|
445
|
-
return best ?? { ...rect, ratio: format };
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
// src/engine/nodes/local/lib/picture-memo.ts
|
|
449
|
-
var ids = /* @__PURE__ */ new WeakMap();
|
|
450
|
-
var nextId = 0;
|
|
451
|
-
function pictureId(picture) {
|
|
452
|
-
const known = ids.get(picture);
|
|
453
|
-
if (known !== void 0) return known;
|
|
454
|
-
const id = nextId++;
|
|
455
|
-
ids.set(picture, id);
|
|
456
|
-
return id;
|
|
457
|
-
}
|
|
458
|
-
function remembered() {
|
|
459
|
-
const readings = /* @__PURE__ */ new WeakMap();
|
|
460
|
-
return (picture, what, read) => {
|
|
461
|
-
const known = readings.get(picture) ?? /* @__PURE__ */ new Map();
|
|
462
|
-
readings.set(picture, known);
|
|
463
|
-
const kept = known.get(what);
|
|
464
|
-
if (kept !== void 0) return kept;
|
|
465
|
-
const value = read();
|
|
466
|
-
known.set(what, value);
|
|
467
|
-
return value;
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
// src/engine/nodes/local/lib/words-band.ts
|
|
472
|
-
var GROUND_REACH = 0.06;
|
|
473
|
-
var LAID = 24;
|
|
474
|
-
var BAND_EDGE = 0.5;
|
|
475
|
-
var BAND_CLEAR = 0.1;
|
|
476
|
-
var SOLID = 0.7;
|
|
477
|
-
var RING = 0.01;
|
|
478
|
-
var LOOK_STRIP = 0.1;
|
|
479
|
-
var greyAt = (img, i) => 0.299 * (img.data[i * img.channels] ?? 0) + 0.587 * (img.data[i * img.channels + 1] ?? 0) + 0.114 * (img.data[i * img.channels + 2] ?? 0);
|
|
480
|
-
function laidMap(aligned, cut) {
|
|
481
|
-
const { width, height } = cut;
|
|
482
|
-
const laid = new Uint8Array(width * height);
|
|
483
|
-
for (let i = 0; i < laid.length; i++) laid[i] = Math.abs(greyAt(aligned, i) - greyAt(cut, i)) > LAID ? 1 : 0;
|
|
484
|
-
return { laid, width, height };
|
|
485
|
-
}
|
|
486
|
-
var rectOf = (s, width, height) => ({
|
|
487
|
-
x0: Math.floor(s.x0 * width),
|
|
488
|
-
y0: Math.floor(s.y0 * height),
|
|
489
|
-
x1: Math.ceil(s.x1 * width),
|
|
490
|
-
y1: Math.ceil(s.y1 * height)
|
|
491
|
-
});
|
|
492
|
-
var clipped = (r, width, height) => ({
|
|
493
|
-
x0: Math.max(0, r.x0),
|
|
494
|
-
y0: Math.max(0, r.y0),
|
|
495
|
-
x1: Math.min(width, r.x1),
|
|
496
|
-
y1: Math.min(height, r.y1)
|
|
497
|
-
});
|
|
498
|
-
var holds = (r, x, y) => x >= r.x0 && x < r.x1 && y >= r.y0 && y < r.y1;
|
|
499
|
-
function laidIn(m, r) {
|
|
500
|
-
const c = clipped(r, m.width, m.height);
|
|
501
|
-
let laid = 0;
|
|
502
|
-
for (let y = c.y0; y < c.y1; y++) for (let x = c.x0; x < c.x1; x++) laid += m.laid[y * m.width + x] ?? 0;
|
|
503
|
-
return { laid, area: Math.max(0, c.x1 - c.x0) * Math.max(0, c.y1 - c.y0) };
|
|
504
|
-
}
|
|
505
|
-
function share(m, r) {
|
|
506
|
-
const { laid, area: area2 } = laidIn(m, r);
|
|
507
|
-
return area2 > 0 ? laid / area2 : 0;
|
|
508
|
-
}
|
|
509
|
-
function laidAround(m, box) {
|
|
510
|
-
const reach = {
|
|
511
|
-
x0: box.x0 - GROUND_REACH,
|
|
512
|
-
y0: box.y0 - GROUND_REACH,
|
|
513
|
-
x1: box.x1 + GROUND_REACH,
|
|
514
|
-
y1: box.y1 + GROUND_REACH
|
|
515
|
-
};
|
|
516
|
-
const outer = laidIn(m, rectOf(reach, m.width, m.height));
|
|
517
|
-
const inner = laidIn(m, rectOf(box, m.width, m.height));
|
|
518
|
-
const ring = outer.area - inner.area;
|
|
519
|
-
return ring > 0 ? (outer.laid - inner.laid) / ring : 0;
|
|
520
|
-
}
|
|
521
|
-
function past(r, side, depth) {
|
|
522
|
-
if (side === 0) return { ...r, y0: r.y0 - depth, y1: r.y0 };
|
|
523
|
-
if (side === 1) return { ...r, y0: r.y1, y1: r.y1 + depth };
|
|
524
|
-
if (side === 2) return { ...r, x0: r.x0 - depth, x1: r.x0 };
|
|
525
|
-
return { ...r, x0: r.x1, x1: r.x1 + depth };
|
|
526
|
-
}
|
|
527
|
-
function grown(r, side) {
|
|
528
|
-
if (side === 0) return { ...r, y0: r.y0 - 1 };
|
|
529
|
-
if (side === 1) return { ...r, y1: r.y1 + 1 };
|
|
530
|
-
if (side === 2) return { ...r, x0: r.x0 - 1 };
|
|
531
|
-
return { ...r, x1: r.x1 + 1 };
|
|
532
|
-
}
|
|
533
|
-
var atFrameEdge = (m, r, side) => side === 0 ? r.y0 <= 0 : side === 1 ? r.y1 >= m.height : side === 2 ? r.x0 <= 0 : r.x1 >= m.width;
|
|
534
|
-
function bandUnder(m, box) {
|
|
535
|
-
const block = clipped(rectOf(box, m.width, m.height), m.width, m.height);
|
|
536
|
-
const inset = Math.min(Math.floor((block.y1 - block.y0) / 4), Math.floor((block.x1 - block.x0 - 1) / 2));
|
|
537
|
-
const start = { x0: block.x0 + inset, y0: block.y0 + inset, x1: block.x1 - inset, y1: block.y1 - inset };
|
|
538
|
-
let r = start;
|
|
539
|
-
for (let grew = true; grew; ) {
|
|
540
|
-
grew = false;
|
|
541
|
-
for (let side = 0; side < 4; side++) {
|
|
542
|
-
if (atFrameEdge(m, r, side) || share(m, past(r, side, 1)) < BAND_EDGE) continue;
|
|
543
|
-
r = grown(r, side);
|
|
544
|
-
grew = true;
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
const outgrown = [r.y0 < start.y0, r.y1 > start.y1, r.x0 < start.x0, r.x1 > start.x1];
|
|
548
|
-
const depth = Math.max(2, Math.round(RING * Math.max(m.width, m.height)));
|
|
549
|
-
for (let side = 0; side < 4; side++) {
|
|
550
|
-
if (!(outgrown[side] || atFrameEdge(m, r, side)) || share(m, past(r, side, depth)) >= BAND_CLEAR) return null;
|
|
551
|
-
}
|
|
552
|
-
if (share(m, r) < SOLID) return null;
|
|
553
|
-
return { x0: r.x0 / m.width, y0: r.y0 / m.height, x1: r.x1 / m.width, y1: r.y1 / m.height };
|
|
554
|
-
}
|
|
555
|
-
function sumsOf(g, strip, skip, ch) {
|
|
556
|
-
const { width } = g.cut;
|
|
557
|
-
const sums = { n: 0, x: 0, y: 0, xx: 0, xy: 0 };
|
|
558
|
-
for (let y = strip.y0; y < strip.y1; y++) {
|
|
559
|
-
for (let x = strip.x0; x < strip.x1; x++) {
|
|
560
|
-
if (skip.some((r) => holds(r, x, y))) continue;
|
|
561
|
-
const i = y * width + x;
|
|
562
|
-
const u = g.cut.data[i * g.cut.channels + ch] ?? 0;
|
|
563
|
-
const v = g.aligned.data[i * g.aligned.channels + ch] ?? 0;
|
|
564
|
-
sums.n++;
|
|
565
|
-
sums.x += u;
|
|
566
|
-
sums.y += v;
|
|
567
|
-
sums.xx += u * u;
|
|
568
|
-
sums.xy += u * v;
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
return sums;
|
|
572
|
-
}
|
|
573
|
-
function toneOf(g, strip, skip) {
|
|
574
|
-
const c = clipped(strip, g.cut.width, g.cut.height);
|
|
575
|
-
return [0, 1, 2].map((ch) => {
|
|
576
|
-
const s = sumsOf(g, c, skip, ch);
|
|
577
|
-
if (s.n === 0) return { a: 0, b: 0 };
|
|
578
|
-
const spread = s.xx - s.x * s.x / s.n;
|
|
579
|
-
const a = spread > s.n ? Math.min(1, Math.max(0, (s.xy - s.x * s.y / s.n) / spread)) : 0;
|
|
580
|
-
return { a, b: (s.y - a * s.x) / s.n };
|
|
581
|
-
});
|
|
582
|
-
}
|
|
583
|
-
var LIGHT_DEPTH = 0.02;
|
|
584
|
-
function ringPast(r, dx, dy, depth) {
|
|
585
|
-
if (dy < 0) return { ...r, y0: r.y1, y1: r.y1 + depth };
|
|
586
|
-
if (dy > 0) return { ...r, y0: r.y0 - depth, y1: r.y0 };
|
|
587
|
-
if (dx < 0) return { ...r, x0: r.x1, x1: r.x1 + depth };
|
|
588
|
-
return { ...r, x0: r.x0 - depth, x1: r.x0 };
|
|
589
|
-
}
|
|
590
|
-
function lightOn(g, ring) {
|
|
591
|
-
const { width, height } = g.cut;
|
|
592
|
-
const c = clipped(ring, width, height);
|
|
593
|
-
return [0, 1, 2].map((ch) => {
|
|
594
|
-
const ratios = [];
|
|
595
|
-
for (let y = c.y0; y < c.y1; y++) {
|
|
596
|
-
for (let x = c.x0; x < c.x1; x++) {
|
|
597
|
-
const i = y * width + x;
|
|
598
|
-
const under = g.cut.data[i * g.cut.channels + ch] ?? 0;
|
|
599
|
-
if (under >= 16) ratios.push((g.aligned.data[i * g.aligned.channels + ch] ?? 0) / under);
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
ratios.sort((a, b) => a - b);
|
|
603
|
-
return Math.min(2, Math.max(0.5, ratios[Math.floor(ratios.length / 2)] ?? 1));
|
|
604
|
-
});
|
|
605
|
-
}
|
|
606
|
-
function edgeLeft(r, dx, dy, width, height) {
|
|
607
|
-
const deep = Math.max(1, Math.round(LOOK_STRIP * (r.y1 - r.y0)));
|
|
608
|
-
const wide = Math.max(1, Math.round(LOOK_STRIP * (r.x1 - r.x0)));
|
|
609
|
-
const across2 = r.x0 <= 2 && r.x1 >= width - 2;
|
|
610
|
-
const down = r.y0 <= 2 && r.y1 >= height - 2;
|
|
611
|
-
if (dy < 0 && across2 && r.y1 >= height - 2)
|
|
612
|
-
return { strip: { ...r, y0: r.y1 - deep }, zone: { ...r, y0: r.y1 + dy } };
|
|
613
|
-
if (dy > 0 && across2 && r.y0 <= 2) return { strip: { ...r, y1: r.y0 + deep }, zone: { ...r, y1: r.y0 + dy } };
|
|
614
|
-
if (dx < 0 && down && r.x1 >= width - 2) return { strip: { ...r, x0: r.x1 - wide }, zone: { ...r, x0: r.x1 + dx } };
|
|
615
|
-
if (dx > 0 && down && r.x0 <= 2) return { strip: { ...r, x1: r.x0 + wide }, zone: { ...r, x1: r.x0 + dx } };
|
|
616
|
-
return null;
|
|
617
|
-
}
|
|
618
|
-
var CORNER = 0.5;
|
|
619
|
-
function floodCorner(shape, w, c, reach, bare) {
|
|
620
|
-
const near = (x, y) => (x - c.x) * c.sx >= 0 && (x - c.x) * c.sx < reach && (y - c.y) * c.sy >= 0 && (y - c.y) * c.sy < reach;
|
|
621
|
-
shape[c.y * w + c.x] = 0;
|
|
622
|
-
const stack = [c.y * w + c.x];
|
|
623
|
-
for (let at = stack.pop(); at !== void 0; at = stack.pop()) {
|
|
624
|
-
const x = at % w;
|
|
625
|
-
const y = (at - x) / w;
|
|
626
|
-
for (const [nx, ny] of [
|
|
627
|
-
[x + 1, y],
|
|
628
|
-
[x - 1, y],
|
|
629
|
-
[x, y + 1],
|
|
630
|
-
[x, y - 1]
|
|
631
|
-
]) {
|
|
632
|
-
if (!near(nx, ny) || shape[ny * w + nx] === 0 || !bare(nx, ny)) continue;
|
|
633
|
-
shape[ny * w + nx] = 0;
|
|
634
|
-
stack.push(ny * w + nx);
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
function shapeOf(g, r) {
|
|
639
|
-
const w = r.x1 - r.x0;
|
|
640
|
-
const h = r.y1 - r.y0;
|
|
641
|
-
const shape = new Uint8Array(Math.max(0, w * h)).fill(1);
|
|
642
|
-
if (w <= 0 || h <= 0) return shape;
|
|
643
|
-
const reach = Math.floor(CORNER * Math.min(w, h));
|
|
644
|
-
const bare = (x, y) => {
|
|
645
|
-
const i = (r.y0 + y) * g.cut.width + r.x0 + x;
|
|
646
|
-
return Math.abs(greyAt(g.aligned, i) - greyAt(g.cut, i)) <= LAID;
|
|
647
|
-
};
|
|
648
|
-
const corners = [
|
|
649
|
-
{ x: 0, y: 0, sx: 1, sy: 1, open: r.x0 > 0 && r.y0 > 0 },
|
|
650
|
-
{ x: w - 1, y: 0, sx: -1, sy: 1, open: r.x1 < g.cut.width && r.y0 > 0 },
|
|
651
|
-
{ x: 0, y: h - 1, sx: 1, sy: -1, open: r.x0 > 0 && r.y1 < g.cut.height },
|
|
652
|
-
{ x: w - 1, y: h - 1, sx: -1, sy: -1, open: r.x1 < g.cut.width && r.y1 < g.cut.height }
|
|
653
|
-
];
|
|
654
|
-
for (const c of corners) if (c.open && bare(c.x, c.y)) floodCorner(shape, w, c, reach, bare);
|
|
655
|
-
return shape;
|
|
656
|
-
}
|
|
657
|
-
function eachOf(r, shape, at) {
|
|
658
|
-
const w = r.x1 - r.x0;
|
|
659
|
-
for (let k = 0; k < shape.length; k++) if (shape[k]) at(r.x0 + k % w, r.y0 + Math.floor(k / w));
|
|
660
|
-
}
|
|
661
|
-
var SOFT_EDGE = 3e-3;
|
|
662
|
-
function eachIn(r, at) {
|
|
663
|
-
for (let y = r.y0; y < r.y1; y++) for (let x = r.x0; x < r.x1; x++) at(x, y);
|
|
664
|
-
}
|
|
665
|
-
function moveBand(g, move) {
|
|
666
|
-
const { width, height } = g.cut;
|
|
667
|
-
const r = clipped(rectOf(move.band, width, height), width, height);
|
|
668
|
-
const soft = Math.max(1, Math.round(SOFT_EDGE * Math.max(width, height)));
|
|
669
|
-
const left = clipped({ x0: r.x0 - soft, y0: r.y0 - soft, x1: r.x1 + soft, y1: r.y1 + soft }, width, height);
|
|
670
|
-
const dx = Math.round(move.dx * width);
|
|
671
|
-
const dy = Math.round(move.dy * height);
|
|
672
|
-
const data = Buffer.alloc(width * height * 3);
|
|
673
|
-
if (g.out.channels === 3) g.out.data.copy(data, 0, 0, width * height * 3);
|
|
674
|
-
else
|
|
675
|
-
for (let i = 0; i < width * height; i++) g.out.data.copy(data, i * 3, i * g.out.channels, i * g.out.channels + 3);
|
|
676
|
-
const shape = shapeOf(g, r);
|
|
677
|
-
const edge = edgeLeft(r, dx, dy, width, height);
|
|
678
|
-
const tone = edge ? toneOf(
|
|
679
|
-
g,
|
|
680
|
-
edge.strip,
|
|
681
|
-
move.words.map((w) => rectOf(w, width, height))
|
|
682
|
-
) : [];
|
|
683
|
-
const light = lightOn(g, ringPast(left, dx, dy, Math.max(4, Math.round(LIGHT_DEPTH * height))));
|
|
684
|
-
eachIn(left, (x, y) => {
|
|
685
|
-
const i = y * width + x;
|
|
686
|
-
const band = edge !== null && holds(edge.zone, x, y);
|
|
687
|
-
for (let ch = 0; ch < 3; ch++) {
|
|
688
|
-
const under = g.cut.data[i * g.cut.channels + ch] ?? 0;
|
|
689
|
-
const t = tone[ch] ?? { a: 0, b: 0 };
|
|
690
|
-
const v = band ? t.a * under + t.b : under * (light[ch] ?? 1);
|
|
691
|
-
data[i * 3 + ch] = Math.max(0, Math.min(255, Math.round(v)));
|
|
692
|
-
}
|
|
693
|
-
});
|
|
694
|
-
eachOf(r, shape, (x, y) => {
|
|
695
|
-
const to = { x: x + dx, y: y + dy };
|
|
696
|
-
if (to.x < 0 || to.y < 0 || to.x >= width || to.y >= height) return;
|
|
697
|
-
const from = y * width + x;
|
|
698
|
-
g.aligned.data.copy(data, (to.y * width + to.x) * 3, from * g.aligned.channels, from * g.aligned.channels + 3);
|
|
699
|
-
});
|
|
700
|
-
return { data, width, height, channels: 3 };
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
// src/engine/nodes/local/lib/safe-fit.ts
|
|
704
|
-
import sharp3 from "sharp";
|
|
705
|
-
var FIT_HEADROOM = 0.01;
|
|
706
|
-
var MIN_FIT_SCALE = 0.85;
|
|
707
|
-
var MIN_FLAT_FIT_SCALE = 0.65;
|
|
708
|
-
function boxesToPlace(boxes, format, opts = {}) {
|
|
709
|
-
if (!opts.copy) return [...boxes];
|
|
710
|
-
const placements = blockingPlacements(opts.placements);
|
|
711
|
-
const past2 = (box) => bandViolations([box], format, { placements })[0];
|
|
712
|
-
const cut = (box) => (past2(box)?.clearance ?? 1) <= FRAME_CUT;
|
|
713
|
-
const logoWords = opts.copy.filter((l) => roleKey(l.role) === "logo").map((l) => l.text);
|
|
714
|
-
const read = withWordmark(boxes, logoWords);
|
|
715
|
-
const buttons = new Set(opts.copy.filter(isButtonLine).flatMap((line) => boxesOfLine(line, read)));
|
|
716
|
-
return shownBoxes(read, opts.copy, (box) => !past2(box)).filter((shown) => !shown.supporting || cut(shown.box)).map((shown) => buttons.has(shown.box) ? buttonShape(shown.box) : shown.box);
|
|
717
|
-
}
|
|
718
|
-
function targetArea(format, placements) {
|
|
719
|
-
const area2 = blockingArea(format, placements);
|
|
720
|
-
const covered = area2.covered;
|
|
721
|
-
if (!covered) return area2;
|
|
722
|
-
return { ...area2, top: covered.top, bottom: covered.bottom, ...covered.rail ? { rail: covered.rail } : {} };
|
|
723
|
-
}
|
|
724
|
-
function planSafeFit(boxes, format, opts = {}) {
|
|
725
|
-
const area2 = targetArea(format, opts.placements);
|
|
726
|
-
const violations = bandViolations(boxes, format, { placements: blockingPlacements(opts.placements) });
|
|
727
|
-
if (!violations.some((v) => v.covered || v.clearance <= FRAME_CUT)) return null;
|
|
728
|
-
const rail = area2.rail && boxes.some((b) => b.y1 > (area2.rail?.from ?? 1)) ? area2.rail.width : 0;
|
|
729
|
-
const union = {
|
|
730
|
-
x0: Math.min(...boxes.map((b) => b.x0)),
|
|
731
|
-
y0: Math.min(...boxes.map((b) => b.y0)),
|
|
732
|
-
x1: Math.max(...boxes.map((b) => b.x1)),
|
|
733
|
-
y1: Math.max(...boxes.map((b) => b.y1))
|
|
734
|
-
};
|
|
735
|
-
const band = {
|
|
736
|
-
x0: area2.left + FIT_HEADROOM,
|
|
737
|
-
y0: area2.top + FIT_HEADROOM,
|
|
738
|
-
x1: 1 - Math.max(area2.right, rail) - FIT_HEADROOM,
|
|
739
|
-
y1: 1 - area2.bottom - FIT_HEADROOM
|
|
740
|
-
};
|
|
741
|
-
const scale = Math.min(1, (band.y1 - band.y0) / (union.y1 - union.y0), (band.x1 - band.x0) / (union.x1 - union.x0));
|
|
742
|
-
if (opts.contain) return containedFit(union, band, opts.minScale ?? MIN_FIT_SCALE, scale);
|
|
743
|
-
if (scale < (opts.minScale ?? MIN_FIT_SCALE)) return null;
|
|
744
|
-
return {
|
|
745
|
-
scale,
|
|
746
|
-
x: offset(scale, union.x0, union.x1, band.x0, band.x1),
|
|
747
|
-
y: offset(scale, union.y0, union.y1, band.y0, band.y1)
|
|
748
|
-
};
|
|
749
|
-
}
|
|
750
|
-
var CONTAIN_STEP = 5e-3;
|
|
751
|
-
function containedFit(union, band, minScale, from) {
|
|
752
|
-
const start = (scale, lo0, hi0, b0, b1) => {
|
|
753
|
-
const lo = Math.max(0, b0 - scale * lo0);
|
|
754
|
-
const hi = Math.min(1 - scale, b1 - scale * hi0);
|
|
755
|
-
if (lo > hi + 1e-9) return null;
|
|
756
|
-
return Math.min(hi, Math.max(lo, 0));
|
|
757
|
-
};
|
|
758
|
-
for (let scale = Math.min(1, from); scale >= minScale - 1e-9; scale -= CONTAIN_STEP) {
|
|
759
|
-
const x = start(scale, union.x0, union.x1, band.x0, band.x1);
|
|
760
|
-
const y = start(scale, union.y0, union.y1, band.y0, band.y1);
|
|
761
|
-
if (x !== null && y !== null) {
|
|
762
|
-
const cx = Math.min(
|
|
763
|
-
Math.min(1 - scale, band.x1 - scale * union.x1),
|
|
764
|
-
Math.max(Math.max(0, band.x0 - scale * union.x0), (1 - scale) / 2)
|
|
765
|
-
);
|
|
766
|
-
return { scale, x: cx, y };
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
return null;
|
|
770
|
-
}
|
|
771
|
-
function offset(scale, from, to, bandFrom, bandTo) {
|
|
772
|
-
const lo = bandFrom - scale * from;
|
|
773
|
-
const hi = bandTo - scale * to;
|
|
774
|
-
if (lo > hi) return (lo + hi) / 2;
|
|
775
|
-
return Math.min(hi, Math.max(lo, (1 - scale) / 2));
|
|
776
|
-
}
|
|
777
|
-
var PLAIN_DEPTH = 0.03;
|
|
778
|
-
var PLAIN_DEPTH_STEP = 36;
|
|
779
|
-
var PLAIN_JUMP = 60;
|
|
780
|
-
var PLAIN_JUMPS = 2;
|
|
781
|
-
function edgeBorders(img, side) {
|
|
782
|
-
const along = side === "top" || side === "bottom" ? img.width : img.height;
|
|
783
|
-
const across2 = side === "top" || side === "bottom" ? img.height : img.width;
|
|
784
|
-
const depth = Math.max(1, Math.round(PLAIN_DEPTH * across2));
|
|
785
|
-
const at = (i, d) => {
|
|
786
|
-
const inward = side === "top" || side === "left" ? d : across2 - 1 - d;
|
|
787
|
-
const [x, y] = side === "top" || side === "bottom" ? [i, inward] : [inward, i];
|
|
788
|
-
return (y * img.width + x) * img.channels;
|
|
789
|
-
};
|
|
790
|
-
const step = (a, b) => {
|
|
791
|
-
let sum = 0;
|
|
792
|
-
for (let c = 0; c < Math.min(3, img.channels); c++)
|
|
793
|
-
sum += Math.abs((img.data[a + c] ?? 0) - (img.data[b + c] ?? 0));
|
|
794
|
-
return sum;
|
|
795
|
-
};
|
|
796
|
-
let jumps = 0;
|
|
797
|
-
for (let i = 0; i < along; i++) {
|
|
798
|
-
for (let d = 1; d <= depth; d++) if (step(at(i, 0), at(i, d)) > PLAIN_DEPTH_STEP) return null;
|
|
799
|
-
if (i > 0 && step(at(i - 1, 0), at(i, 0)) > PLAIN_JUMP) jumps++;
|
|
800
|
-
}
|
|
801
|
-
return jumps;
|
|
802
|
-
}
|
|
803
|
-
function plainEdge(img, side) {
|
|
804
|
-
const borders = edgeBorders(img, side);
|
|
805
|
-
return borders !== null && borders <= PLAIN_JUMPS;
|
|
806
|
-
}
|
|
807
|
-
async function minFitScale(image) {
|
|
808
|
-
const { data, info } = await sharp3(image).removeAlpha().raw().toBuffer({ resolveWithObject: true });
|
|
809
|
-
const raw = { data, width: info.width, height: info.height, channels: info.channels };
|
|
810
|
-
const flat = ["top", "bottom", "left", "right"].every((side) => edgeBorders(raw, side) === 0);
|
|
811
|
-
return flat ? MIN_FLAT_FIT_SCALE : MIN_FIT_SCALE;
|
|
812
|
-
}
|
|
813
|
-
async function fitPicture(image, fit) {
|
|
814
|
-
const { width = 0, height = 0 } = await sharp3(image).metadata();
|
|
815
|
-
const sw = Math.max(1, Math.round(width * fit.scale));
|
|
816
|
-
const sh = Math.max(1, Math.round(height * fit.scale));
|
|
817
|
-
const px = Math.round(fit.x * width);
|
|
818
|
-
const py = Math.round(fit.y * height);
|
|
819
|
-
const left = Math.max(0, -px);
|
|
820
|
-
const top = Math.max(0, -py);
|
|
821
|
-
const w = Math.min(sw, width - px) - left;
|
|
822
|
-
const h = Math.min(sh, height - py) - top;
|
|
823
|
-
const shrunk2 = await sharp3(image).removeAlpha().resize(sw, sh, { fit: "fill", kernel: "lanczos3" }).extract({ left, top, width: w, height: h }).raw().toBuffer({ resolveWithObject: true });
|
|
824
|
-
const pad = { left: Math.max(0, px), top: Math.max(0, py) };
|
|
825
|
-
const extend = { ...pad, right: width - pad.left - w, bottom: height - pad.top - h };
|
|
826
|
-
const raw = { data: shrunk2.data, width: w, height: h, channels: shrunk2.info.channels };
|
|
827
|
-
const sides = ["top", "bottom", "left", "right"].filter((side) => extend[side] > 0);
|
|
828
|
-
if (!sides.every((side) => plainEdge(raw, side))) return null;
|
|
829
|
-
return sharp3(shrunk2.data, { raw: { width: w, height: h, channels: shrunk2.info.channels } }).extend({ ...extend, extendWith: "copy" }).png().toBuffer();
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
// src/engine/nodes/local/lib/words-layer.ts
|
|
833
|
-
var inSpans = (spans, fx, fy) => spans.some((s) => fx >= s.x0 && fx <= s.x1 && fy >= s.y0 && fy <= s.y1);
|
|
834
|
-
function centresIn(lo, hi, size) {
|
|
835
|
-
const inside = new Uint8Array(size);
|
|
836
|
-
for (let t = 0; t < size; t++) {
|
|
837
|
-
const f = (t + 0.5) / size;
|
|
838
|
-
inside[t] = f >= lo && f <= hi ? 1 : 0;
|
|
839
|
-
}
|
|
840
|
-
return inside;
|
|
841
|
-
}
|
|
842
|
-
function spanMask(spans, width, height) {
|
|
843
|
-
const mask = new Uint8Array(width * height);
|
|
844
|
-
for (const s of spans) {
|
|
845
|
-
const across2 = centresIn(s.x0, s.x1, width);
|
|
846
|
-
const down = centresIn(s.y0, s.y1, height);
|
|
847
|
-
for (let y = 0; y < height; y++) {
|
|
848
|
-
if (down[y]) for (let x = 0; x < width; x++) mask[y * width + x] ||= across2[x] ?? 0;
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
return mask;
|
|
852
|
-
}
|
|
853
|
-
function taps(u, v, width, height) {
|
|
854
|
-
const x = Math.min(width - 1, Math.max(0, u));
|
|
855
|
-
const y = Math.min(height - 1, Math.max(0, v));
|
|
856
|
-
const x0 = Math.floor(x);
|
|
857
|
-
const y0 = Math.floor(y);
|
|
858
|
-
const x1 = Math.min(width - 1, x0 + 1);
|
|
859
|
-
const y1 = Math.min(height - 1, y0 + 1);
|
|
860
|
-
const fx = x - x0;
|
|
861
|
-
const fy = y - y0;
|
|
862
|
-
return {
|
|
863
|
-
at: [y0 * width + x0, y0 * width + x1, y1 * width + x0, y1 * width + x1],
|
|
864
|
-
w: [(1 - fx) * (1 - fy), fx * (1 - fy), (1 - fx) * fy, fx * fy]
|
|
865
|
-
};
|
|
866
|
-
}
|
|
867
|
-
function tapped(values, t, stride = 1, c = 0, missing = 0) {
|
|
868
|
-
let sum = 0;
|
|
869
|
-
for (let k = 0; k < 4; k++) sum += (t.w[k] ?? 0) * (values[(t.at[k] ?? 0) * stride + c] ?? missing);
|
|
870
|
-
return sum;
|
|
871
|
-
}
|
|
872
|
-
function grayOf(img) {
|
|
873
|
-
const data = new Float32Array(img.width * img.height);
|
|
874
|
-
for (let i = 0; i < data.length; i++) {
|
|
875
|
-
const o = i * img.channels;
|
|
876
|
-
data[i] = 0.299 * (img.data[o] ?? 0) + 0.587 * (img.data[o + 1] ?? 0) + 0.114 * (img.data[o + 2] ?? 0);
|
|
877
|
-
}
|
|
878
|
-
return { data, width: img.width, height: img.height };
|
|
879
|
-
}
|
|
880
|
-
function shrink(g, edge) {
|
|
881
|
-
const f = Math.max(1, Math.ceil(Math.max(g.width, g.height) / edge));
|
|
882
|
-
if (f === 1) return g;
|
|
883
|
-
const width = Math.floor(g.width / f);
|
|
884
|
-
const height = Math.floor(g.height / f);
|
|
885
|
-
const data = new Float32Array(width * height);
|
|
886
|
-
for (let i = 0; i < width * height; i++) {
|
|
887
|
-
const x = i % width * f;
|
|
888
|
-
const y = Math.floor(i / width) * f;
|
|
889
|
-
let sum = 0;
|
|
890
|
-
for (let j = 0; j < f; j++) {
|
|
891
|
-
const row = (y + j) * g.width + x;
|
|
892
|
-
for (let k = 0; k < f; k++) sum += g.data[row + k] ?? 0;
|
|
893
|
-
}
|
|
894
|
-
data[i] = sum / (f * f);
|
|
895
|
-
}
|
|
896
|
-
return { data, width, height };
|
|
897
|
-
}
|
|
898
|
-
var JUDGED_MAX = 2e4;
|
|
899
|
-
function judgedPixels(g, exclude) {
|
|
900
|
-
const mx = Math.ceil(g.width * 0.04);
|
|
901
|
-
const my = Math.ceil(g.height * 0.04);
|
|
902
|
-
const step = g.width * g.height > JUDGED_MAX ? 2 : 1;
|
|
903
|
-
const out = [];
|
|
904
|
-
for (let i = 0; i < g.width * g.height; i++) {
|
|
905
|
-
const x = i % g.width;
|
|
906
|
-
const y = Math.floor(i / g.width);
|
|
907
|
-
if (x % step !== 0 || y % step !== 0) continue;
|
|
908
|
-
if (x < mx || y < my || x >= g.width - mx || y >= g.height - my) continue;
|
|
909
|
-
if (!inSpans(exclude, x / g.width, y / g.height)) out.push(i);
|
|
910
|
-
}
|
|
911
|
-
const rows = [];
|
|
912
|
-
const starts = [];
|
|
913
|
-
out.forEach((i, j) => {
|
|
914
|
-
const y = Math.floor(i / g.width);
|
|
915
|
-
if (rows[rows.length - 1] === y) return;
|
|
916
|
-
rows.push(y);
|
|
917
|
-
starts.push(j);
|
|
918
|
-
});
|
|
919
|
-
starts.push(out.length);
|
|
920
|
-
return {
|
|
921
|
-
at: Int32Array.from(out),
|
|
922
|
-
xs: Int32Array.from(out, (i) => i % g.width),
|
|
923
|
-
rows: Int32Array.from(rows),
|
|
924
|
-
starts: Int32Array.from(starts)
|
|
925
|
-
};
|
|
926
|
-
}
|
|
927
|
-
function axisOf(origin, scale, count, size) {
|
|
928
|
-
const near = new Int32Array(count);
|
|
929
|
-
const at = new Int32Array(count);
|
|
930
|
-
const frac = new Float64Array(count);
|
|
931
|
-
for (let t = 0; t < count; t++) {
|
|
932
|
-
const p = origin + scale * t;
|
|
933
|
-
near[t] = Math.min(size - 1, Math.max(0, Math.round(p)));
|
|
934
|
-
const q = Math.min(size - 1.001, Math.max(0, p));
|
|
935
|
-
at[t] = q | 0;
|
|
936
|
-
frac[t] = q - (at[t] ?? 0);
|
|
937
|
-
}
|
|
938
|
-
return { near, at, frac };
|
|
939
|
-
}
|
|
940
|
-
var MISFIT_CAP = 40;
|
|
941
|
-
var LINEUP_FLAT = 1.5;
|
|
942
|
-
function withFlat(g) {
|
|
943
|
-
const flat = new Uint8Array(g.width * g.height);
|
|
944
|
-
for (let i = 0; i < flat.length; i++) {
|
|
945
|
-
const x = i % g.width;
|
|
946
|
-
const y = (i - x) / g.width;
|
|
947
|
-
if (x === 0 || y === 0 || x === g.width - 1 || y === g.height - 1) continue;
|
|
948
|
-
let sum = 0;
|
|
949
|
-
let square = 0;
|
|
950
|
-
for (let k = 0; k < 9; k++) {
|
|
951
|
-
const v = g.data[(y + Math.floor(k / 3) - 1) * g.width + x + k % 3 - 1] ?? 0;
|
|
952
|
-
sum += v;
|
|
953
|
-
square += v * v;
|
|
954
|
-
}
|
|
955
|
-
flat[i] = Math.sqrt(Math.max(0, square / 9 - (sum / 9) ** 2)) < LINEUP_FLAT ? 1 : 0;
|
|
956
|
-
}
|
|
957
|
-
return { ...g, flat };
|
|
958
|
-
}
|
|
959
|
-
function misfit(cut, render, judged, k, c, cap = MISFIT_CAP, beat = Number.POSITIVE_INFINITY, above = Number.POSITIVE_INFINITY) {
|
|
960
|
-
const cx = cut.width / 2;
|
|
961
|
-
const cy = cut.height / 2;
|
|
962
|
-
const ox = cx - c.scale * cx + c.dx * k;
|
|
963
|
-
const oy = cy - c.scale * cy + c.dy * k;
|
|
964
|
-
const cols = axisOf(ox, c.scale, cut.width, render.width);
|
|
965
|
-
const rows = axisOf(oy, c.scale, cut.height, render.height);
|
|
966
|
-
const look = { cut, render, judged, cols, most: cap * cap };
|
|
967
|
-
const count = judged.at.length;
|
|
968
|
-
const acc = { sum: 0, n: 0 };
|
|
969
|
-
for (let r = 0; r < judged.rows.length; r++) {
|
|
970
|
-
const from = judged.starts[r] ?? 0;
|
|
971
|
-
const least = acc.sum / (acc.n + count - from);
|
|
972
|
-
if (least >= beat || least > above) return Number.POSITIVE_INFINITY;
|
|
973
|
-
rowMisfit(look, rows, judged.rows[r] ?? 0, from, judged.starts[r + 1] ?? count, acc);
|
|
974
|
-
}
|
|
975
|
-
return acc.n > 0 ? acc.sum / acc.n : Number.POSITIVE_INFINITY;
|
|
976
|
-
}
|
|
977
|
-
function bilinear(data, i, width, fx, fy) {
|
|
978
|
-
const top = (data[i] ?? 0) * (1 - fx) + (data[i + 1] ?? 0) * fx;
|
|
979
|
-
const bottom = (data[i + width] ?? 0) * (1 - fx) + (data[i + width + 1] ?? 0) * fx;
|
|
980
|
-
return top * (1 - fy) + bottom * fy;
|
|
981
|
-
}
|
|
982
|
-
function rowMisfit(s, rows, y, from, to, acc) {
|
|
983
|
-
const { width, data, flat } = s.render;
|
|
984
|
-
const { near, at, frac } = s.cols;
|
|
985
|
-
const flatRow = (rows.near[y] ?? 0) * width;
|
|
986
|
-
const row = (rows.at[y] ?? 0) * width;
|
|
987
|
-
const fy = rows.frac[y] ?? 0;
|
|
988
|
-
for (let j = from; j < to; j++) {
|
|
989
|
-
const x = s.judged.xs[j] ?? 0;
|
|
990
|
-
if (flat?.[flatRow + (near[x] ?? 0)]) continue;
|
|
991
|
-
const d = (s.cut.data[s.judged.at[j] ?? 0] ?? 0) - bilinear(data, row + (at[x] ?? 0), width, frac[x] ?? 0, fy);
|
|
992
|
-
acc.sum += Math.min(d * d, s.most);
|
|
993
|
-
acc.n++;
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
var MAX_SHIFT = 0.03;
|
|
997
|
-
var MAX_SCALE_OFF = 0.03;
|
|
998
|
-
var LEVELS = [
|
|
999
|
-
{ edge: 128, scales: [-0.03, -0.02, -0.01, 0, 0.01, 0.02, 0.03], reach: null },
|
|
1000
|
-
{ edge: 256, scales: [-5e-3, 0, 5e-3], reach: 2 },
|
|
1001
|
-
{ edge: 512, scales: [-25e-4, 0, 25e-4], reach: 1 }
|
|
1002
|
-
];
|
|
1003
|
-
function searchLevel(cut, render, k, level, centre, exclude) {
|
|
1004
|
-
const judged = judgedPixels(cut, exclude);
|
|
1005
|
-
const reach = level.reach ?? Math.ceil(MAX_SHIFT * Math.max(cut.width, cut.height));
|
|
1006
|
-
let best = centre;
|
|
1007
|
-
let score = Number.POSITIVE_INFINITY;
|
|
1008
|
-
const atCentre = misfit(cut, render, judged, k, centre);
|
|
1009
|
-
const side = 2 * reach + 1;
|
|
1010
|
-
for (const ds of level.scales) {
|
|
1011
|
-
const scale = centre.scale + ds;
|
|
1012
|
-
if (Math.abs(scale - 1) > MAX_SCALE_OFF + 1e-9) continue;
|
|
1013
|
-
for (let s = 0; s < side * side; s++) {
|
|
1014
|
-
const c = { scale, dx: centre.dx + (s % side - reach) / k, dy: centre.dy + (Math.floor(s / side) - reach) / k };
|
|
1015
|
-
const m = misfit(cut, render, judged, k, c, MISFIT_CAP, score, atCentre);
|
|
1016
|
-
if (m < score) {
|
|
1017
|
-
score = m;
|
|
1018
|
-
best = c;
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
return { best, score };
|
|
1023
|
-
}
|
|
1024
|
-
function registerToSource(render, cut, exclude) {
|
|
1025
|
-
return lineUps(render, `${pictureId(cut)} ${JSON.stringify(exclude)}`, () => lineUp(render, cut, exclude));
|
|
1026
|
-
}
|
|
1027
|
-
var lineUps = remembered();
|
|
1028
|
-
var warps = remembered();
|
|
1029
|
-
function lineUp(render, cut, exclude) {
|
|
1030
|
-
const full = { cut: grayOf(cut), render: grayOf(render) };
|
|
1031
|
-
let best = { scale: 1, dx: 0, dy: 0 };
|
|
1032
|
-
let finest = { cut: full.cut, render: full.render, k: 1 };
|
|
1033
|
-
for (const level of LEVELS) {
|
|
1034
|
-
const c = shrink(full.cut, level.edge);
|
|
1035
|
-
const r = withFlat(shrink(full.render, level.edge));
|
|
1036
|
-
const k = c.width / cut.width;
|
|
1037
|
-
best = searchLevel(c, r, k, level, best, exclude).best;
|
|
1038
|
-
finest = { cut: c, render: r, k };
|
|
1039
|
-
}
|
|
1040
|
-
const judged = judgedPixels(finest.cut, exclude);
|
|
1041
|
-
const residual = misfit(finest.cut, finest.render, judged, finest.k, best, Number.POSITIVE_INFINITY);
|
|
1042
|
-
return { ...best, residual: Math.sqrt(residual) };
|
|
1043
|
-
}
|
|
1044
|
-
function bilinearAxis(centre, scale, shift, size) {
|
|
1045
|
-
const lo = new Int32Array(size);
|
|
1046
|
-
const hi = new Int32Array(size);
|
|
1047
|
-
const frac = new Float64Array(size);
|
|
1048
|
-
for (let t = 0; t < size; t++) {
|
|
1049
|
-
const p = Math.min(size - 1, Math.max(0, centre + scale * (t - centre) + shift));
|
|
1050
|
-
lo[t] = Math.floor(p);
|
|
1051
|
-
hi[t] = Math.min(size - 1, (lo[t] ?? 0) + 1);
|
|
1052
|
-
frac[t] = p - (lo[t] ?? 0);
|
|
1053
|
-
}
|
|
1054
|
-
return { lo, hi, frac };
|
|
1055
|
-
}
|
|
1056
|
-
function warpToCut(render, fit) {
|
|
1057
|
-
return warps(render, `${fit.scale} ${fit.dx} ${fit.dy}`, () => warped(render, fit));
|
|
1058
|
-
}
|
|
1059
|
-
function warped(render, fit) {
|
|
1060
|
-
const { width, height } = render;
|
|
1061
|
-
const out = Buffer.alloc(width * height * 3);
|
|
1062
|
-
const cols = bilinearAxis(width / 2, fit.scale, fit.dx, width);
|
|
1063
|
-
const rows = bilinearAxis(height / 2, fit.scale, fit.dy, height);
|
|
1064
|
-
for (let y = 0; y < height; y++) {
|
|
1065
|
-
const row = { y, top: (rows.lo[y] ?? 0) * width, bottom: (rows.hi[y] ?? 0) * width, fy: rows.frac[y] ?? 0 };
|
|
1066
|
-
warpRow(render, out, cols, row);
|
|
1067
|
-
}
|
|
1068
|
-
return { data: out, width, height, channels: 3 };
|
|
1069
|
-
}
|
|
1070
|
-
function warpRow(render, out, cols, row) {
|
|
1071
|
-
const { width, channels, data } = render;
|
|
1072
|
-
const { fy } = row;
|
|
1073
|
-
for (let x = 0; x < width; x++) {
|
|
1074
|
-
const fx = cols.frac[x] ?? 0;
|
|
1075
|
-
const w = [(1 - fx) * (1 - fy), fx * (1 - fy), (1 - fx) * fy, fx * fy];
|
|
1076
|
-
const lo = cols.lo[x] ?? 0;
|
|
1077
|
-
const hi = cols.hi[x] ?? 0;
|
|
1078
|
-
const a = [
|
|
1079
|
-
(row.top + lo) * channels,
|
|
1080
|
-
(row.top + hi) * channels,
|
|
1081
|
-
(row.bottom + lo) * channels,
|
|
1082
|
-
(row.bottom + hi) * channels
|
|
1083
|
-
];
|
|
1084
|
-
const o = (row.y * width + x) * 3;
|
|
1085
|
-
for (let c = 0; c < 3; c++) {
|
|
1086
|
-
out[o + c] = Math.round(
|
|
1087
|
-
w[0] * (data[a[0] + c] ?? 0) + w[1] * (data[a[1] + c] ?? 0) + w[2] * (data[a[2] + c] ?? 0) + w[3] * (data[a[3] + c] ?? 0)
|
|
1088
|
-
);
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1092
|
-
function emptyLayer(width, height) {
|
|
1093
|
-
return {
|
|
1094
|
-
width,
|
|
1095
|
-
height,
|
|
1096
|
-
alpha: new Float32Array(width * height),
|
|
1097
|
-
ink: new Float32Array(width * height),
|
|
1098
|
-
pixels: Buffer.alloc(width * height * 3),
|
|
1099
|
-
gain: new Float32Array(width * height * 3).fill(1)
|
|
1100
|
-
};
|
|
1101
|
-
}
|
|
1102
|
-
var CELL = 8;
|
|
1103
|
-
var CHANGED = 14;
|
|
1104
|
-
var MATTE_LO = 8;
|
|
1105
|
-
var MATTE_HI = 32;
|
|
1106
|
-
var BOX_PAD = 0.3;
|
|
1107
|
-
var TOUCH = 0.02;
|
|
1108
|
-
var GAIN_MAX = 4;
|
|
1109
|
-
var TONE_MIN_PIXELS = CELL * CELL / 4;
|
|
1110
|
-
var padded = (b, width, height) => {
|
|
1111
|
-
const pad = BOX_PAD * (b.y1 - b.y0);
|
|
1112
|
-
const padX = pad * height / width;
|
|
1113
|
-
return { x0: b.x0 - padX, y0: b.y0 - pad, x1: b.x1 + padX, y1: b.y1 + pad };
|
|
1114
|
-
};
|
|
1115
|
-
function gridOf(width, height) {
|
|
1116
|
-
const gw = Math.ceil(width / CELL);
|
|
1117
|
-
const gh = Math.ceil(height / CELL);
|
|
1118
|
-
return {
|
|
1119
|
-
gw,
|
|
1120
|
-
gh,
|
|
1121
|
-
cell: (x, y) => Math.floor(y / CELL) * gw + Math.floor(x / CELL),
|
|
1122
|
-
span: (g) => {
|
|
1123
|
-
const gx = g % gw;
|
|
1124
|
-
const gy = Math.floor(g / gw);
|
|
1125
|
-
return {
|
|
1126
|
-
x0: gx * CELL / width,
|
|
1127
|
-
y0: gy * CELL / height,
|
|
1128
|
-
x1: (gx + 1) * CELL / width,
|
|
1129
|
-
y1: (gy + 1) * CELL / height
|
|
1130
|
-
};
|
|
1131
|
-
}
|
|
1132
|
-
};
|
|
1133
|
-
}
|
|
1134
|
-
function neighbours2(g, grid) {
|
|
1135
|
-
const gx = g % grid.gw;
|
|
1136
|
-
const gy = Math.floor(g / grid.gw);
|
|
1137
|
-
const out = [];
|
|
1138
|
-
for (let k = 0; k < 9; k++) {
|
|
1139
|
-
const nx = gx + k % 3 - 1;
|
|
1140
|
-
const ny = gy + Math.floor(k / 3) - 1;
|
|
1141
|
-
if (k !== 4 && nx >= 0 && ny >= 0 && nx < grid.gw && ny < grid.gh) out.push(ny * grid.gw + nx);
|
|
1142
|
-
}
|
|
1143
|
-
return out;
|
|
1144
|
-
}
|
|
1145
|
-
function addPixel(s, aligned, cut, i, g, tone) {
|
|
1146
|
-
let d = 0;
|
|
1147
|
-
for (let c = 0; c < 3; c++) {
|
|
1148
|
-
const a = aligned.data[i * aligned.channels + c] ?? 0;
|
|
1149
|
-
const b = cut.data[i * cut.channels + c] ?? 0;
|
|
1150
|
-
d = Math.max(d, Math.abs(a - b));
|
|
1151
|
-
if (!tone) continue;
|
|
1152
|
-
s.meanA[g * 3 + c] = (s.meanA[g * 3 + c] ?? 0) + a;
|
|
1153
|
-
s.meanC[g * 3 + c] = (s.meanC[g * 3 + c] ?? 0) + b;
|
|
1154
|
-
}
|
|
1155
|
-
return d;
|
|
1156
|
-
}
|
|
1157
|
-
function cellStats(aligned, cut, words, grid) {
|
|
1158
|
-
const cells = grid.gw * grid.gh;
|
|
1159
|
-
const s = {
|
|
1160
|
-
diff: new Float32Array(cells),
|
|
1161
|
-
pixelDiff: new Float32Array(cut.width * cut.height),
|
|
1162
|
-
meanA: new Float32Array(cells * 3),
|
|
1163
|
-
meanC: new Float32Array(cells * 3),
|
|
1164
|
-
toneCount: new Float32Array(cells)
|
|
1165
|
-
};
|
|
1166
|
-
const count = new Float32Array(cells);
|
|
1167
|
-
for (let i = 0; i < cut.width * cut.height; i++) {
|
|
1168
|
-
const x = i % cut.width;
|
|
1169
|
-
const y = Math.floor(i / cut.width);
|
|
1170
|
-
const g = grid.cell(x, y);
|
|
1171
|
-
const tone = !words[i];
|
|
1172
|
-
const d = addPixel(s, aligned, cut, i, g, tone);
|
|
1173
|
-
s.pixelDiff[i] = d;
|
|
1174
|
-
s.diff[g] = (s.diff[g] ?? 0) + d;
|
|
1175
|
-
count[g] = (count[g] ?? 0) + 1;
|
|
1176
|
-
if (tone) s.toneCount[g] = (s.toneCount[g] ?? 0) + 1;
|
|
1177
|
-
}
|
|
1178
|
-
for (let k = 0; k < cells * 3; k++) {
|
|
1179
|
-
const g = Math.floor(k / 3);
|
|
1180
|
-
if (k % 3 === 0) s.diff[g] = (s.diff[g] ?? 0) / (count[g] || 1);
|
|
1181
|
-
s.meanA[k] = (s.meanA[k] ?? 0) / (s.toneCount[g] || 1);
|
|
1182
|
-
s.meanC[k] = (s.meanC[k] ?? 0) / (s.toneCount[g] || 1);
|
|
1183
|
-
}
|
|
1184
|
-
return s;
|
|
1185
|
-
}
|
|
1186
|
-
var isChanged = (diff, g) => (diff[g] ?? 0) >= CHANGED;
|
|
1187
|
-
function flood(start, id, diff, region, grid) {
|
|
1188
|
-
region[start] = id;
|
|
1189
|
-
const stack = [start];
|
|
1190
|
-
while (stack.length > 0) {
|
|
1191
|
-
const g = stack.pop() ?? 0;
|
|
1192
|
-
const next = neighbours2(g, grid).filter((n) => isChanged(diff, n) && (region[n] ?? -1) < 0);
|
|
1193
|
-
for (const n of next) region[n] = id;
|
|
1194
|
-
stack.push(...next);
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
1197
|
-
function changedRegions(diff, grid) {
|
|
1198
|
-
const region = new Int32Array(diff.length).fill(-1);
|
|
1199
|
-
let count = 0;
|
|
1200
|
-
for (let start = 0; start < diff.length; start++) {
|
|
1201
|
-
if (!isChanged(diff, start) || (region[start] ?? -1) >= 0) continue;
|
|
1202
|
-
flood(start, count, diff, region, grid);
|
|
1203
|
-
count++;
|
|
1204
|
-
}
|
|
1205
|
-
return { region, count };
|
|
1206
|
-
}
|
|
1207
|
-
function cellGain(s, g) {
|
|
1208
|
-
if ((s.toneCount[g] ?? 0) < TONE_MIN_PIXELS) return null;
|
|
1209
|
-
return [0, 1, 2].map((c) => {
|
|
1210
|
-
const ratio = ((s.meanA[g * 3 + c] ?? 0) + 8) / ((s.meanC[g * 3 + c] ?? 0) + 8);
|
|
1211
|
-
return Math.min(GAIN_MAX, Math.max(1 / GAIN_MAX, ratio));
|
|
1212
|
-
});
|
|
1213
|
-
}
|
|
1214
|
-
function neighbourGain(gain, known, g, grid) {
|
|
1215
|
-
const around = neighbours2(g, grid).filter((n) => known[n]);
|
|
1216
|
-
if (around.length === 0) return null;
|
|
1217
|
-
return [0, 1, 2].map((c) => around.reduce((sum, n) => sum + (gain[n * 3 + c] ?? 1), 0) / around.length);
|
|
1218
|
-
}
|
|
1219
|
-
function spreadGains(gain, known, grid) {
|
|
1220
|
-
const filled = [];
|
|
1221
|
-
for (let g = 0; g < known.length; g++) {
|
|
1222
|
-
const from = known[g] ? null : neighbourGain(gain, known, g, grid);
|
|
1223
|
-
if (from) filled.push([g, from]);
|
|
1224
|
-
}
|
|
1225
|
-
for (const [g, value] of filled) {
|
|
1226
|
-
gain.set(value, g * 3);
|
|
1227
|
-
known[g] = 1;
|
|
1228
|
-
}
|
|
1229
|
-
return filled.length;
|
|
1230
|
-
}
|
|
1231
|
-
function toneGains(s, grid) {
|
|
1232
|
-
const cells = grid.gw * grid.gh;
|
|
1233
|
-
const gain = new Float32Array(cells * 3).fill(1);
|
|
1234
|
-
const known = new Uint8Array(cells);
|
|
1235
|
-
for (let g = 0; g < cells; g++) {
|
|
1236
|
-
const own = cellGain(s, g);
|
|
1237
|
-
if (!own) continue;
|
|
1238
|
-
gain.set(own, g * 3);
|
|
1239
|
-
known[g] = 1;
|
|
1240
|
-
}
|
|
1241
|
-
let spread = 1;
|
|
1242
|
-
while (spread > 0) spread = spreadGains(gain, known, grid);
|
|
1243
|
-
return gain;
|
|
1244
|
-
}
|
|
1245
|
-
function smoothGains(gains, grid) {
|
|
1246
|
-
const out = new Float32Array(gains.length);
|
|
1247
|
-
for (let g = 0; g < grid.gw * grid.gh; g++) {
|
|
1248
|
-
const around = [g, ...neighbours2(g, grid)];
|
|
1249
|
-
for (let c = 0; c < 3; c++) {
|
|
1250
|
-
out[g * 3 + c] = around.reduce((sum, n) => sum + (gains[n * 3 + c] ?? 1), 0) / around.length;
|
|
1251
|
-
}
|
|
1252
|
-
}
|
|
1253
|
-
return out;
|
|
1254
|
-
}
|
|
1255
|
-
var STRUCTURAL = 16;
|
|
1256
|
-
function toneMisfit(aligned, cut, gains, words, grid) {
|
|
1257
|
-
const sum = new Float32Array(grid.gw * grid.gh);
|
|
1258
|
-
const count = new Float32Array(grid.gw * grid.gh);
|
|
1259
|
-
for (let i = 0; i < cut.width * cut.height; i++) {
|
|
1260
|
-
const x = i % cut.width;
|
|
1261
|
-
const y = (i - x) / cut.width;
|
|
1262
|
-
if (words[i]) continue;
|
|
1263
|
-
const g = grid.cell(x, y);
|
|
1264
|
-
let d = 0;
|
|
1265
|
-
for (let c = 0; c < 3; c++) {
|
|
1266
|
-
const want = Math.min(255, (cut.data[i * cut.channels + c] ?? 0) * (gains[g * 3 + c] ?? 1));
|
|
1267
|
-
d = Math.max(d, Math.abs((aligned.data[i * aligned.channels + c] ?? 0) - want));
|
|
1268
|
-
}
|
|
1269
|
-
sum[g] = (sum[g] ?? 0) + d;
|
|
1270
|
-
count[g] = (count[g] ?? 0) + 1;
|
|
1271
|
-
}
|
|
1272
|
-
return sum.map((v, g) => count[g] ? v / (count[g] ?? 1) : 0);
|
|
1273
|
-
}
|
|
1274
|
-
function cellTaps(grid, x, y) {
|
|
1275
|
-
return taps((x + 0.5) / CELL - 0.5, (y + 0.5) / CELL - 0.5, grid.gw, grid.gh);
|
|
1276
|
-
}
|
|
1277
|
-
function wordsLayer(aligned, cut, designed, opts = {}) {
|
|
1278
|
-
const { width, height } = cut;
|
|
1279
|
-
const grid = gridOf(width, height);
|
|
1280
|
-
const boxes = designed.map((b) => padded(b, width, height));
|
|
1281
|
-
const tight = designed.map((b) => ({ ...b }));
|
|
1282
|
-
const inBoxes2 = spanMask(boxes, width, height);
|
|
1283
|
-
const stats = cellStats(aligned, cut, inBoxes2, grid);
|
|
1284
|
-
const kept = keptChange(stats.diff, boxes, grid, TOUCH * width / height);
|
|
1285
|
-
const gains = smoothGains(toneGains(stats, grid), grid);
|
|
1286
|
-
const misfit2 = opts.boxesOnly ? null : toneMisfit(aligned, cut, gains, inBoxes2, grid);
|
|
1287
|
-
const tone = kept.tone.map((t, g) => t > 0 && misfit2 && (misfit2[g] ?? 0) < STRUCTURAL ? 1 : 0);
|
|
1288
|
-
const dropped = kept.tone.reduce((n, t, g) => n + (t > 0 && !tone[g] ? 1 : 0), 0);
|
|
1289
|
-
const reverted = kept.reverted + dropped / kept.tone.length;
|
|
1290
|
-
const look = {
|
|
1291
|
-
aligned,
|
|
1292
|
-
stats,
|
|
1293
|
-
grid,
|
|
1294
|
-
boxes: inBoxes2,
|
|
1295
|
-
tone,
|
|
1296
|
-
gains,
|
|
1297
|
-
tight: spanMask(tight, width, height),
|
|
1298
|
-
subjects: spanMask(opts.subjects ?? [], width, height),
|
|
1299
|
-
keepOut: spanMask(opts.keepOut ?? [], width, height)
|
|
1300
|
-
};
|
|
1301
|
-
const layer = emptyLayer(width, height);
|
|
1302
|
-
for (let i = 0; i < width * height; i++) setLayerPixel(layer, look, i);
|
|
1303
|
-
return { layer, reverted };
|
|
1304
|
-
}
|
|
1305
|
-
var overlaps = (a, b) => a.x0 < b.x1 && a.x1 > b.x0 && a.y0 < b.y1 && a.y1 > b.y0;
|
|
1306
|
-
function keptChange(diff, boxes, grid, touchY) {
|
|
1307
|
-
const { region, count } = changedRegions(diff, grid);
|
|
1308
|
-
const near = boxes.map((b) => ({ x0: b.x0 - TOUCH, y0: b.y0 - touchY, x1: b.x1 + TOUCH, y1: b.y1 + touchY }));
|
|
1309
|
-
const kept = new Uint8Array(count);
|
|
1310
|
-
for (let g = 0; g < region.length; g++) {
|
|
1311
|
-
const id = region[g] ?? -1;
|
|
1312
|
-
if (id >= 0 && near.some((b) => overlaps(grid.span(g), b))) kept[id] = 1;
|
|
1313
|
-
}
|
|
1314
|
-
const tone = new Float32Array(region.length);
|
|
1315
|
-
let reverted = 0;
|
|
1316
|
-
for (let g = 0; g < region.length; g++) {
|
|
1317
|
-
const id = region[g] ?? -1;
|
|
1318
|
-
if (id < 0) continue;
|
|
1319
|
-
if (kept[id]) tone[g] = 1;
|
|
1320
|
-
else if (!boxes.some((b) => overlaps(grid.span(g), b))) reverted++;
|
|
1321
|
-
}
|
|
1322
|
-
return { tone, reverted: reverted / region.length };
|
|
1323
|
-
}
|
|
1324
|
-
function setLayerPixel(layer, look, i) {
|
|
1325
|
-
const x = i % layer.width;
|
|
1326
|
-
const y = Math.floor(i / layer.width);
|
|
1327
|
-
const onSubject = look.subjects[i] === 1;
|
|
1328
|
-
const inBox = (onSubject ? look.tight : look.boxes)[i] === 1;
|
|
1329
|
-
if (!inBox && look.keepOut[i]) return;
|
|
1330
|
-
const near = look.tone[look.grid.cell(x, y)] ? cellTaps(look.grid, x, y) : void 0;
|
|
1331
|
-
const toned = near ? tapped(look.tone, near, 1, 0, 1) : 0;
|
|
1332
|
-
const changed = ((look.stats.pixelDiff[i] ?? 0) - MATTE_LO) / (MATTE_HI - MATTE_LO);
|
|
1333
|
-
const matte = inBox ? Math.min(1, Math.max(0, changed)) : 0;
|
|
1334
|
-
const a = Math.max(matte, toned);
|
|
1335
|
-
if (a <= 0) return;
|
|
1336
|
-
layer.alpha[i] = a;
|
|
1337
|
-
layer.ink[i] = inBox ? 1 : 0;
|
|
1338
|
-
const cells = near ?? cellTaps(look.grid, x, y);
|
|
1339
|
-
for (let c = 0; c < 3; c++) {
|
|
1340
|
-
layer.pixels[i * 3 + c] = look.aligned.data[i * look.aligned.channels + c] ?? 0;
|
|
1341
|
-
layer.gain[i * 3 + c] = tapped(look.gains, cells, 3, c, 1);
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
1344
|
-
function protectFaces(layer, faces) {
|
|
1345
|
-
if (faces.length === 0) return { layer, covered: 0 };
|
|
1346
|
-
const alpha = Float32Array.from(layer.alpha);
|
|
1347
|
-
const ink = Float32Array.from(layer.ink);
|
|
1348
|
-
let onFace = 0;
|
|
1349
|
-
let covered = 0;
|
|
1350
|
-
for (let i = 0; i < alpha.length; i++) {
|
|
1351
|
-
const fx = (i % layer.width + 0.5) / layer.width;
|
|
1352
|
-
const fy = (Math.floor(i / layer.width) + 0.5) / layer.height;
|
|
1353
|
-
if (!inSpans(faces, fx, fy)) continue;
|
|
1354
|
-
onFace++;
|
|
1355
|
-
const k = ink[i] ?? 0;
|
|
1356
|
-
if (k <= 0 || (alpha[i] ?? 0) <= 0) continue;
|
|
1357
|
-
covered++;
|
|
1358
|
-
alpha[i] = (alpha[i] ?? 0) * (1 - k);
|
|
1359
|
-
ink[i] = 0;
|
|
1360
|
-
}
|
|
1361
|
-
return { layer: { ...layer, alpha, ink }, covered: onFace > 0 ? covered / onFace : 0 };
|
|
1362
|
-
}
|
|
1363
|
-
var HIDDEN = 0.5;
|
|
1364
|
-
function coveredSubjects(layer, subjects) {
|
|
1365
|
-
return subjects.filter((b) => b.kind === "product" || b.kind === "animal").filter((b) => {
|
|
1366
|
-
const x0 = Math.max(0, Math.floor(b.x0 * layer.width));
|
|
1367
|
-
const x1 = Math.min(layer.width, Math.ceil(b.x1 * layer.width));
|
|
1368
|
-
const y0 = Math.max(0, Math.floor(b.y0 * layer.height));
|
|
1369
|
-
const y1 = Math.min(layer.height, Math.ceil(b.y1 * layer.height));
|
|
1370
|
-
let covered = 0;
|
|
1371
|
-
for (let y = y0; y < y1; y++) {
|
|
1372
|
-
for (let x = x0; x < x1; x++) if ((layer.alpha[y * layer.width + x] ?? 0) >= 0.5) covered++;
|
|
1373
|
-
}
|
|
1374
|
-
const all = (x1 - x0) * (y1 - y0);
|
|
1375
|
-
return all > 0 && covered / all > HIDDEN;
|
|
1376
|
-
}).map((b) => b.label);
|
|
1377
|
-
}
|
|
1378
|
-
function compose(cut, layer) {
|
|
1379
|
-
const { width, height } = cut;
|
|
1380
|
-
const out = Buffer.alloc(width * height * 3);
|
|
1381
|
-
for (let k = 0; k < width * height * 3; k++) {
|
|
1382
|
-
const i = Math.floor(k / 3);
|
|
1383
|
-
const c = k % 3;
|
|
1384
|
-
const base = cut.data[i * cut.channels + c] ?? 0;
|
|
1385
|
-
const a = layer.alpha[i] ?? 0;
|
|
1386
|
-
if (a <= 0) {
|
|
1387
|
-
out[k] = base;
|
|
1388
|
-
continue;
|
|
1389
|
-
}
|
|
1390
|
-
const ink = layer.ink[i] ?? 0;
|
|
1391
|
-
const toned = Math.min(255, base * (layer.gain[k] ?? 1));
|
|
1392
|
-
out[k] = Math.round(base * (1 - a) + (ink * (layer.pixels[k] ?? 0) + (1 - ink) * toned) * a);
|
|
1393
|
-
}
|
|
1394
|
-
return { data: out, width, height, channels: 3 };
|
|
1395
|
-
}
|
|
1396
|
-
function identityOutside(out, cut, alpha) {
|
|
1397
|
-
let sum = 0;
|
|
1398
|
-
let n = 0;
|
|
1399
|
-
for (let k = 0; k < cut.width * cut.height * 3; k++) {
|
|
1400
|
-
const i = Math.floor(k / 3);
|
|
1401
|
-
if ((alpha[i] ?? 0) > 0) continue;
|
|
1402
|
-
sum += Math.abs((out.data[i * out.channels + k % 3] ?? 0) - (cut.data[i * cut.channels + k % 3] ?? 0));
|
|
1403
|
-
n++;
|
|
1404
|
-
}
|
|
1405
|
-
return n > 0 ? sum / n : 0;
|
|
1406
|
-
}
|
|
1407
|
-
var MIN_WORDS_SCALE = 0.8;
|
|
1408
|
-
var MAX_WORDS_MOVE = 0.12;
|
|
1409
|
-
var MAX_BAND_MOVE = 0.25;
|
|
1410
|
-
var GROUP_GAP = 0.04;
|
|
1411
|
-
var SCALE_STEP = 0.05;
|
|
1412
|
-
var MOVE_STEP = 5e-3;
|
|
1413
|
-
var into = (from, to, lo, hi) => Math.max(lo - from, 0) + Math.min(hi - to, 0);
|
|
1414
|
-
var unionOf3 = (boxes) => ({
|
|
1415
|
-
x0: Math.min(...boxes.map((b) => b.x0)),
|
|
1416
|
-
y0: Math.min(...boxes.map((b) => b.y0)),
|
|
1417
|
-
x1: Math.max(...boxes.map((b) => b.x1)),
|
|
1418
|
-
y1: Math.max(...boxes.map((b) => b.y1))
|
|
1419
|
-
});
|
|
1420
|
-
var overlap = (a, b) => a.x0 < b.x1 && a.x1 > b.x0 && a.y0 < b.y1 && a.y1 > b.y0;
|
|
1421
|
-
function clearAreas(format, placements, bottom) {
|
|
1422
|
-
const area2 = blockingArea(format, placements);
|
|
1423
|
-
const rail = area2.rail && bottom > area2.rail.from ? area2.rail.width : 0;
|
|
1424
|
-
const band = {
|
|
1425
|
-
x0: area2.left + FIT_HEADROOM,
|
|
1426
|
-
y0: area2.top + FIT_HEADROOM,
|
|
1427
|
-
x1: 1 - Math.max(area2.right, rail) - FIT_HEADROOM,
|
|
1428
|
-
y1: 1 - area2.bottom - FIT_HEADROOM
|
|
1429
|
-
};
|
|
1430
|
-
const covered = area2.covered;
|
|
1431
|
-
if (!covered) return [band];
|
|
1432
|
-
const drawn = covered.rail && bottom > covered.rail.from ? covered.rail.width : 0;
|
|
1433
|
-
return [
|
|
1434
|
-
band,
|
|
1435
|
-
{
|
|
1436
|
-
x0: band.x0,
|
|
1437
|
-
y0: covered.top + FIT_HEADROOM,
|
|
1438
|
-
x1: 1 - Math.max(area2.right, drawn) - FIT_HEADROOM,
|
|
1439
|
-
y1: 1 - covered.bottom - FIT_HEADROOM
|
|
1440
|
-
}
|
|
1441
|
-
];
|
|
1442
|
-
}
|
|
1443
|
-
function wordGroups(words) {
|
|
1444
|
-
const sorted = [...words].sort((a, b) => a.y0 - b.y0);
|
|
1445
|
-
const groups = [];
|
|
1446
|
-
for (const box of sorted) {
|
|
1447
|
-
const last = groups.at(-1);
|
|
1448
|
-
if (last && box.y0 - Math.max(...last.map((b) => b.y1)) < GROUP_GAP) last.push(box);
|
|
1449
|
-
else groups.push([box]);
|
|
1450
|
-
}
|
|
1451
|
-
return groups;
|
|
1452
|
-
}
|
|
1453
|
-
function placed(union, fit, area2, p, withFaces) {
|
|
1454
|
-
const m = movedSpan(union, fit);
|
|
1455
|
-
const inArea = m.x0 >= area2.x0 - 1e-9 && m.x1 <= area2.x1 + 1e-9 && m.y0 >= area2.y0 - 1e-9 && m.y1 <= area2.y1 + 1e-9;
|
|
1456
|
-
return inArea && !p.others.some((o) => overlap(m, o)) && !(withFaces && p.faces.some((f) => overlap(m, f)));
|
|
1457
|
-
}
|
|
1458
|
-
function shiftInto(union, at, area2, p, withFaces) {
|
|
1459
|
-
const half = at.scale * (union.x1 - union.x0) / 2;
|
|
1460
|
-
const dx = into(at.cx - half, at.cx + half, area2.x0, area2.x1);
|
|
1461
|
-
const steps = Math.round(p.reach / MOVE_STEP);
|
|
1462
|
-
for (let k = 0; k <= 2 * steps; k++) {
|
|
1463
|
-
const dy = (k % 2 === 0 ? 1 : -1) * Math.ceil(k / 2) * MOVE_STEP;
|
|
1464
|
-
const fit = { ...at, dx, dy };
|
|
1465
|
-
if (placed(union, fit, area2, p, withFaces)) return fit;
|
|
1466
|
-
}
|
|
1467
|
-
return null;
|
|
1468
|
-
}
|
|
1469
|
-
function blockMove(union, p, withFaces) {
|
|
1470
|
-
const cx = (union.x0 + union.x1) / 2;
|
|
1471
|
-
const cy = (union.y0 + union.y1) / 2;
|
|
1472
|
-
for (let scale = 1; scale >= p.minScale - 1e-9; scale -= SCALE_STEP) {
|
|
1473
|
-
for (const area2 of p.areas) {
|
|
1474
|
-
const fit = shiftInto(union, { scale, cx, cy }, area2, p, withFaces);
|
|
1475
|
-
if (fit) return fit;
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1478
|
-
return null;
|
|
1479
|
-
}
|
|
1480
|
-
var unmoved = (fit) => fit.scale === 1 && fit.dx === 0 && fit.dy === 0;
|
|
1481
|
-
var across = (area2, union) => ({
|
|
1482
|
-
...area2,
|
|
1483
|
-
x0: Math.min(area2.x0, union.x0),
|
|
1484
|
-
x1: Math.max(area2.x1, union.x1)
|
|
1485
|
-
});
|
|
1486
|
-
function planWordsShift(words, format, opts = {}) {
|
|
1487
|
-
const faces = opts.faces ?? [];
|
|
1488
|
-
const obstacles = [...faces, ...opts.keepOut ?? []];
|
|
1489
|
-
const groups = wordGroups(words).map((g) => ({ boxes: g, union: unionOf3(g) }));
|
|
1490
|
-
const moves = [];
|
|
1491
|
-
let reason;
|
|
1492
|
-
for (const [i, group] of groups.entries()) {
|
|
1493
|
-
const violations = bandViolations(group.boxes, format, { placements: blockingPlacements(opts.placements) });
|
|
1494
|
-
const past2 = violations.length > 0;
|
|
1495
|
-
const onFace = faces.some((f) => overlap(group.union, f));
|
|
1496
|
-
if (!past2 && !onFace) continue;
|
|
1497
|
-
const sideways = violations.some((v) => v.edges.includes("left") || v.edges.includes("right"));
|
|
1498
|
-
const p = {
|
|
1499
|
-
areas: clearAreas(format, opts.placements, group.union.y1).map((a) => sideways ? a : across(a, group.union)),
|
|
1500
|
-
faces: obstacles,
|
|
1501
|
-
others: groups.filter((_, j) => j !== i).map((g) => g.union),
|
|
1502
|
-
minScale: opts.minScale ?? MIN_WORDS_SCALE,
|
|
1503
|
-
reach: past2 ? MAX_BAND_MOVE : MAX_WORDS_MOVE
|
|
1504
|
-
};
|
|
1505
|
-
const step = stepOf(group.union, past2, p);
|
|
1506
|
-
if (typeof step === "string") reason ??= step;
|
|
1507
|
-
else if (step) moves.push({ group: group.union, fit: step });
|
|
1508
|
-
}
|
|
1509
|
-
return reason ? { moves, reason } : { moves };
|
|
1510
|
-
}
|
|
1511
|
-
function stepOf(union, past2, p) {
|
|
1512
|
-
const fit = blockMove(union, p, true);
|
|
1513
|
-
if (fit) return unmoved(fit) ? null : fit;
|
|
1514
|
-
return blockMove(union, p, false) || !past2 ? "words_over_face" : "band_too_far";
|
|
1515
|
-
}
|
|
1516
|
-
function movedSpan(b, fit) {
|
|
1517
|
-
const at = (v, c, d) => c + fit.scale * (v - c) + d;
|
|
1518
|
-
return {
|
|
1519
|
-
...b,
|
|
1520
|
-
x0: at(b.x0, fit.cx, fit.dx),
|
|
1521
|
-
y0: at(b.y0, fit.cy, fit.dy),
|
|
1522
|
-
x1: at(b.x1, fit.cx, fit.dx),
|
|
1523
|
-
y1: at(b.y1, fit.cy, fit.dy)
|
|
1524
|
-
};
|
|
1525
|
-
}
|
|
1526
|
-
var GROUP_REACH = 0.03;
|
|
1527
|
-
function blockAt(layer, moves, i) {
|
|
1528
|
-
const fx = (i % layer.width + 0.5) / layer.width;
|
|
1529
|
-
const fy = (Math.floor(i / layer.width) + 0.5) / layer.height;
|
|
1530
|
-
return moves.findIndex(
|
|
1531
|
-
(m) => fx >= m.group.x0 - GROUP_REACH && fx <= m.group.x1 + GROUP_REACH && fy >= m.group.y0 - GROUP_REACH && fy <= m.group.y1 + GROUP_REACH
|
|
1532
|
-
);
|
|
1533
|
-
}
|
|
1534
|
-
function owners(layer, moves) {
|
|
1535
|
-
const { width } = layer;
|
|
1536
|
-
const own = new Int16Array(width * layer.height).fill(-1);
|
|
1537
|
-
for (let i = 0; i < own.length; i++) if ((layer.alpha[i] ?? 0) > 0) own[i] = blockAt(layer, moves, i);
|
|
1538
|
-
spreadOwners(own, layer.alpha, width);
|
|
1539
|
-
return own;
|
|
1540
|
-
}
|
|
1541
|
-
function spreadOwners(own, alpha, width) {
|
|
1542
|
-
const { stack, seeds } = seedsOf(own);
|
|
1543
|
-
let top = seeds;
|
|
1544
|
-
const free = (n) => n >= 0 && n < own.length && (own[n] ?? -1) < 0 && (alpha[n] ?? 0) > 0;
|
|
1545
|
-
while (top > 0) {
|
|
1546
|
-
const i = stack[--top] ?? 0;
|
|
1547
|
-
const block = own[i] ?? -1;
|
|
1548
|
-
for (const n of touching(i, width)) {
|
|
1549
|
-
if (!free(n)) continue;
|
|
1550
|
-
own[n] = block;
|
|
1551
|
-
stack[top++] = n;
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1554
|
-
}
|
|
1555
|
-
function seedsOf(own) {
|
|
1556
|
-
const stack = new Int32Array(own.length);
|
|
1557
|
-
let seeds = 0;
|
|
1558
|
-
for (let i = 0; i < own.length; i++) if ((own[i] ?? -1) >= 0) stack[seeds++] = i;
|
|
1559
|
-
return { stack, seeds };
|
|
1560
|
-
}
|
|
1561
|
-
function touching(i, width) {
|
|
1562
|
-
const x = i % width;
|
|
1563
|
-
return [x > 0 ? i - 1 : -1, x < width - 1 ? i + 1 : -1, i - width, i + width];
|
|
1564
|
-
}
|
|
1565
|
-
function takePixel(out, to, layer, t) {
|
|
1566
|
-
const a = tapped(layer.alpha, t);
|
|
1567
|
-
if (a <= (out.alpha[to] ?? 0)) return;
|
|
1568
|
-
out.alpha[to] = a;
|
|
1569
|
-
out.ink[to] = tapped(layer.ink, t);
|
|
1570
|
-
for (let c = 0; c < 3; c++) {
|
|
1571
|
-
out.pixels[to * 3 + c] = Math.round(tapped(layer.pixels, t, 3, c));
|
|
1572
|
-
out.gain[to * 3 + c] = tapped(layer.gain, t, 3, c, 1);
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1575
|
-
function moveBlock(out, layer, own, k, fit) {
|
|
1576
|
-
const { width, height } = layer;
|
|
1577
|
-
const back = (p, c, d, size) => c * size + (p + 0.5 - c * size - d * size) / fit.scale - 0.5;
|
|
1578
|
-
for (let i = 0; i < width * height; i++) {
|
|
1579
|
-
const u = back(i % width, fit.cx, fit.dx, width);
|
|
1580
|
-
const v = back(Math.floor(i / width), fit.cy, fit.dy, height);
|
|
1581
|
-
const inside = u >= 0 && v >= 0 && u <= width - 1 && v <= height - 1;
|
|
1582
|
-
if (inside && own[Math.round(v) * width + Math.round(u)] === k) takePixel(out, i, layer, taps(u, v, width, height));
|
|
1583
|
-
}
|
|
1584
|
-
}
|
|
1585
|
-
function movedPart(layer, moves) {
|
|
1586
|
-
const own = owners(layer, moves);
|
|
1587
|
-
const out = emptyLayer(layer.width, layer.height);
|
|
1588
|
-
for (let i = 0; i < own.length; i++) {
|
|
1589
|
-
if ((own[i] ?? -1) >= 0) takePixel(out, i, layer, { at: [i, i, i, i], w: [1, 0, 0, 0] });
|
|
1590
|
-
}
|
|
1591
|
-
return out;
|
|
1592
|
-
}
|
|
1593
|
-
function moveLayer(layer, moves) {
|
|
1594
|
-
const own = owners(layer, moves);
|
|
1595
|
-
const out = emptyLayer(layer.width, layer.height);
|
|
1596
|
-
for (let i = 0; i < own.length; i++) {
|
|
1597
|
-
if ((own[i] ?? -1) < 0 && (layer.alpha[i] ?? 0) > 0)
|
|
1598
|
-
takePixel(out, i, layer, { at: [i, i, i, i], w: [1, 0, 0, 0] });
|
|
1599
|
-
}
|
|
1600
|
-
for (const [k, { fit }] of moves.entries()) moveBlock(out, layer, own, k, fit);
|
|
1601
|
-
return out;
|
|
1602
|
-
}
|
|
1603
|
-
function cropLayer(layer, rect) {
|
|
1604
|
-
const out = emptyLayer(rect.width, rect.height);
|
|
1605
|
-
for (let y = 0; y < rect.height; y++) {
|
|
1606
|
-
const from = (rect.y + y) * layer.width + rect.x;
|
|
1607
|
-
const to = y * rect.width;
|
|
1608
|
-
out.alpha.set(layer.alpha.subarray(from, from + rect.width), to);
|
|
1609
|
-
out.ink.set(layer.ink.subarray(from, from + rect.width), to);
|
|
1610
|
-
out.gain.set(layer.gain.subarray(from * 3, (from + rect.width) * 3), to * 3);
|
|
1611
|
-
layer.pixels.copy(out.pixels, to * 3, from * 3, (from + rect.width) * 3);
|
|
1612
|
-
}
|
|
1613
|
-
return out;
|
|
1614
|
-
}
|
|
1615
|
-
|
|
1616
|
-
// src/engine/nodes/local/lib/whole-render.ts
|
|
1617
|
-
var inSpans2 = (spans, fx, fy) => spans.some((s) => fx >= s.x0 && fx <= s.x1 && fy >= s.y0 && fy <= s.y1);
|
|
1618
|
-
function blurLine(src, out, start, step, n, r) {
|
|
1619
|
-
const last = start + (n - 1) * step;
|
|
1620
|
-
let acc = 0;
|
|
1621
|
-
for (let k = -r; k <= r; k++) acc += src[k <= 0 ? start : k >= n - 1 ? last : start + k * step] ?? 0;
|
|
1622
|
-
for (let k = 0; k < n; k++) {
|
|
1623
|
-
out[start + k * step] = acc / (2 * r + 1);
|
|
1624
|
-
const enter = k + r + 1;
|
|
1625
|
-
const leave = k - r;
|
|
1626
|
-
acc += (src[enter >= n - 1 ? last : start + enter * step] ?? 0) - (src[leave <= 0 ? start : start + leave * step] ?? 0);
|
|
1627
|
-
}
|
|
1628
|
-
}
|
|
1629
|
-
function blurColumns(src, out, width, height, r) {
|
|
1630
|
-
const acc = new Float64Array(width);
|
|
1631
|
-
const row = (k) => (k <= 0 ? 0 : k >= height - 1 ? height - 1 : k) * width;
|
|
1632
|
-
for (let k = -r; k <= r; k++) {
|
|
1633
|
-
const from = row(k);
|
|
1634
|
-
for (let x = 0; x < width; x++) acc[x] = (acc[x] ?? 0) + (src[from + x] ?? 0);
|
|
1635
|
-
}
|
|
1636
|
-
for (let k = 0; k < height; k++) {
|
|
1637
|
-
const to = k * width;
|
|
1638
|
-
const enter = row(k + r + 1);
|
|
1639
|
-
const leave = row(k - r);
|
|
1640
|
-
for (let x = 0; x < width; x++) {
|
|
1641
|
-
out[to + x] = (acc[x] ?? 0) / (2 * r + 1);
|
|
1642
|
-
acc[x] = (acc[x] ?? 0) + ((src[enter + x] ?? 0) - (src[leave + x] ?? 0));
|
|
1643
|
-
}
|
|
1644
|
-
}
|
|
1645
|
-
}
|
|
1646
|
-
function boxBlur(src, width, height, r) {
|
|
1647
|
-
if (r < 1) return Float32Array.from(src);
|
|
1648
|
-
const along = new Float32Array(width * height);
|
|
1649
|
-
for (let y = 0; y < height; y++) blurLine(src, along, y * width, 1, width, r);
|
|
1650
|
-
const out = new Float32Array(width * height);
|
|
1651
|
-
blurColumns(along, out, width, height, r);
|
|
1652
|
-
return out;
|
|
1653
|
-
}
|
|
1654
|
-
var soften = (src, width, height, r) => boxBlur(boxBlur(src, width, height, r), width, height, r);
|
|
1655
|
-
function channelOf(img, c) {
|
|
1656
|
-
const out = new Float32Array(img.width * img.height);
|
|
1657
|
-
for (let i = 0; i < out.length; i++) out[i] = img.data[i * img.channels + c] ?? 0;
|
|
1658
|
-
return out;
|
|
1659
|
-
}
|
|
1660
|
-
var STRUCTURE_RADIUS = 8e-3;
|
|
1661
|
-
var GAIN_MAX2 = 2.5;
|
|
1662
|
-
var FLAT2 = 2.5;
|
|
1663
|
-
var STILL = 14;
|
|
1664
|
-
var MOVED = 28;
|
|
1665
|
-
function flatOf2(img) {
|
|
1666
|
-
const { width, height } = img;
|
|
1667
|
-
const grey = new Float32Array(width * height);
|
|
1668
|
-
for (let i = 0; i < grey.length; i++) {
|
|
1669
|
-
const o = i * img.channels;
|
|
1670
|
-
grey[i] = 0.299 * (img.data[o] ?? 0) + 0.587 * (img.data[o + 1] ?? 0) + 0.114 * (img.data[o + 2] ?? 0);
|
|
1671
|
-
}
|
|
1672
|
-
const squares = new Float32Array(grey.length);
|
|
1673
|
-
for (let i = 0; i < grey.length; i++) squares[i] = (grey[i] ?? 0) * (grey[i] ?? 0);
|
|
1674
|
-
const mean = boxBlur(grey, width, height, 2);
|
|
1675
|
-
const square = boxBlur(squares, width, height, 2);
|
|
1676
|
-
const flat = new Uint8Array(mean.length);
|
|
1677
|
-
for (let i = 0; i < mean.length; i++) {
|
|
1678
|
-
const m = mean[i] ?? 0;
|
|
1679
|
-
flat[i] = Math.sqrt(Math.max(0, (square[i] ?? 0) - m * m)) < FLAT2 ? 1 : 0;
|
|
1680
|
-
}
|
|
1681
|
-
return flat;
|
|
1682
|
-
}
|
|
1683
|
-
var gridOver = (width, height, size) => ({
|
|
1684
|
-
size,
|
|
1685
|
-
gw: Math.ceil(width / size),
|
|
1686
|
-
gh: Math.ceil(height / size)
|
|
1687
|
-
});
|
|
1688
|
-
function cellOf(grid, i, width) {
|
|
1689
|
-
const x = i % width;
|
|
1690
|
-
return Math.floor((i - x) / width / grid.size) * grid.gw + Math.floor(x / grid.size);
|
|
1691
|
-
}
|
|
1692
|
-
function cellsNear(grid, g, reach) {
|
|
1693
|
-
const gx = g % grid.gw;
|
|
1694
|
-
const gy = (g - gx) / grid.gw;
|
|
1695
|
-
const out = [];
|
|
1696
|
-
for (let ny = Math.max(0, gy - reach); ny <= Math.min(grid.gh - 1, gy + reach); ny++) {
|
|
1697
|
-
for (let nx = Math.max(0, gx - reach); nx <= Math.min(grid.gw - 1, gx + reach); nx++) out.push(ny * grid.gw + nx);
|
|
1698
|
-
}
|
|
1699
|
-
return out;
|
|
1700
|
-
}
|
|
1701
|
-
var SURFACE_CELL = 8;
|
|
1702
|
-
function laidCells(flat, structure, width, grid) {
|
|
1703
|
-
const laid = new Float32Array(grid.gw * grid.gh);
|
|
1704
|
-
const count = new Float32Array(grid.gw * grid.gh);
|
|
1705
|
-
for (let i = 0; i < flat.length; i++) {
|
|
1706
|
-
const g = cellOf(grid, i, width);
|
|
1707
|
-
count[g] = (count[g] ?? 0) + 1;
|
|
1708
|
-
if (flat[i] && (structure[i] ?? 0) > MOVED) laid[g] = (laid[g] ?? 0) + 1;
|
|
1709
|
-
}
|
|
1710
|
-
return Uint8Array.from(laid, (n, g) => n >= (count[g] ?? 1) / 2 ? 1 : 0);
|
|
1711
|
-
}
|
|
1712
|
-
function wordCells(words, width, height, grid) {
|
|
1713
|
-
const out = [];
|
|
1714
|
-
for (let g = 0; g < grid.gw * grid.gh; g++) {
|
|
1715
|
-
const gx = g % grid.gw;
|
|
1716
|
-
const gy = (g - gx) / grid.gw;
|
|
1717
|
-
const x0 = gx * grid.size / width;
|
|
1718
|
-
const y0 = gy * grid.size / height;
|
|
1719
|
-
const x1 = (gx + 1) * grid.size / width;
|
|
1720
|
-
const y1 = (gy + 1) * grid.size / height;
|
|
1721
|
-
if (words.some((w) => w.x0 < x1 && w.x1 > x0 && w.y0 < y1 && w.y1 > y0)) out.push(g);
|
|
1722
|
-
}
|
|
1723
|
-
return out;
|
|
1724
|
-
}
|
|
1725
|
-
function flood2(seeds, open, grid) {
|
|
1726
|
-
const reached = new Uint8Array(grid.gw * grid.gh);
|
|
1727
|
-
for (const g of seeds) reached[g] = 1;
|
|
1728
|
-
const stack = [...seeds];
|
|
1729
|
-
while (stack.length > 0) {
|
|
1730
|
-
const next = cellsNear(grid, stack.pop() ?? 0, 1).filter((n) => !reached[n] && open[n]);
|
|
1731
|
-
for (const n of next) reached[n] = 1;
|
|
1732
|
-
stack.push(...next);
|
|
1733
|
-
}
|
|
1734
|
-
return reached;
|
|
1735
|
-
}
|
|
1736
|
-
function surfaceOf(flat, structure, width, height, words) {
|
|
1737
|
-
const grid = gridOver(width, height, SURFACE_CELL);
|
|
1738
|
-
const reached = flood2(wordCells(words, width, height, grid), laidCells(flat, structure, width, grid), grid);
|
|
1739
|
-
const surface = new Uint8Array(structure.length);
|
|
1740
|
-
for (let y = 0; y < height; y++) {
|
|
1741
|
-
const row = Math.floor(y / grid.size) * grid.gw;
|
|
1742
|
-
for (let x = 0; x < width; x++) {
|
|
1743
|
-
const i = y * width + x;
|
|
1744
|
-
surface[i] = reached[row + Math.floor(x / grid.size)] && (structure[i] ?? 0) > MOVED ? 1 : 0;
|
|
1745
|
-
}
|
|
1746
|
-
}
|
|
1747
|
-
return surface;
|
|
1748
|
-
}
|
|
1749
|
-
var GROUND_LINE = 3;
|
|
1750
|
-
var CARD_TONE = 16;
|
|
1751
|
-
var CARD_RUN = 0.6;
|
|
1752
|
-
var inTone = (img, i, tone) => [0, 1, 2].every((c) => Math.abs((img.data[i * img.channels + c] ?? 0) - (tone[c] ?? 0)) <= CARD_TONE);
|
|
1753
|
-
function* pixelsOf(r, width, height) {
|
|
1754
|
-
for (let y = Math.max(0, r.y0); y < Math.min(height, r.y1); y++) {
|
|
1755
|
-
for (let x = Math.max(0, r.x0); x < Math.min(width, r.x1); x++) yield y * width + x;
|
|
1756
|
-
}
|
|
1757
|
-
}
|
|
1758
|
-
var stripsPast = (r, depth) => [
|
|
1759
|
-
{ ...r, y0: r.y0 - depth, y1: r.y0 },
|
|
1760
|
-
{ ...r, y0: r.y1, y1: r.y1 + depth },
|
|
1761
|
-
{ ...r, x0: r.x0 - depth, x1: r.x0 },
|
|
1762
|
-
{ ...r, x0: r.x1, x1: r.x1 + depth }
|
|
1763
|
-
];
|
|
1764
|
-
var inBoxes = (boxes, i, width) => {
|
|
1765
|
-
const x = i % width;
|
|
1766
|
-
const y = (i - x) / width;
|
|
1767
|
-
return boxes.some((b) => x >= b.x0 && x < b.x1 && y >= b.y0 && y < b.y1);
|
|
1768
|
-
};
|
|
1769
|
-
function cardTone(img, flat, box, others = []) {
|
|
1770
|
-
const { width, height } = img;
|
|
1771
|
-
const around = stripsPast(box, GROUND_LINE).flatMap((s) => [...pixelsOf(s, width, height)]).filter((i) => !inBoxes(others, i, width));
|
|
1772
|
-
const flatOnes = around.filter((i) => flat[i]);
|
|
1773
|
-
if (around.length === 0 || flatOnes.length < around.length / 2) return null;
|
|
1774
|
-
return [0, 1, 2].map((c) => {
|
|
1775
|
-
const values = flatOnes.map((i) => img.data[i * img.channels + c] ?? 0).sort((a, b) => a - b);
|
|
1776
|
-
return values[Math.floor(values.length / 2)] ?? 0;
|
|
1777
|
-
});
|
|
1778
|
-
}
|
|
1779
|
-
var grownBy = (r, side, by) => side === 0 ? { ...r, y0: r.y0 - by } : side === 1 ? { ...r, y1: r.y1 + by } : side === 2 ? { ...r, x0: r.x0 - by } : { ...r, x1: r.x1 + by };
|
|
1780
|
-
function cardAround(img, box, tone) {
|
|
1781
|
-
const { width, height } = img;
|
|
1782
|
-
const share2 = (s) => {
|
|
1783
|
-
const px = [...pixelsOf(s, width, height)];
|
|
1784
|
-
return px.length > 0 ? px.filter((i) => inTone(img, i, tone)).length / px.length : 0;
|
|
1785
|
-
};
|
|
1786
|
-
const runs = (r2, side) => {
|
|
1787
|
-
for (let by = 1; by <= GROUND_LINE + 1; by++)
|
|
1788
|
-
if (share2(stripsPast(grownBy(r2, side, by - 1), 1)[side] ?? r2) >= CARD_RUN) return by;
|
|
1789
|
-
return 0;
|
|
1790
|
-
};
|
|
1791
|
-
let r = box;
|
|
1792
|
-
for (let grew = true; grew; ) {
|
|
1793
|
-
grew = false;
|
|
1794
|
-
for (let side = 0; side < 4; side++) {
|
|
1795
|
-
const by = runs(r, side);
|
|
1796
|
-
if (by === 0) continue;
|
|
1797
|
-
r = grownBy(r, side, by);
|
|
1798
|
-
grew = true;
|
|
1799
|
-
}
|
|
1800
|
-
}
|
|
1801
|
-
return r;
|
|
1802
|
-
}
|
|
1803
|
-
function groundOf(aligned, flat, words) {
|
|
1804
|
-
const { width, height } = aligned;
|
|
1805
|
-
const toned = new Float32Array(width * height);
|
|
1806
|
-
const inCard = new Uint8Array(width * height);
|
|
1807
|
-
const boxes = words.map((w) => ({
|
|
1808
|
-
x0: Math.max(0, Math.floor(w.x0 * width)),
|
|
1809
|
-
y0: Math.max(0, Math.floor(w.y0 * height)),
|
|
1810
|
-
x1: Math.min(width, Math.ceil(w.x1 * width)),
|
|
1811
|
-
y1: Math.min(height, Math.ceil(w.y1 * height))
|
|
1812
|
-
}));
|
|
1813
|
-
for (const box of boxes) {
|
|
1814
|
-
const tone = cardTone(
|
|
1815
|
-
aligned,
|
|
1816
|
-
flat,
|
|
1817
|
-
box,
|
|
1818
|
-
boxes.filter((b) => b !== box)
|
|
1819
|
-
);
|
|
1820
|
-
if (!tone) continue;
|
|
1821
|
-
for (const i of pixelsOf(cardAround(aligned, box, tone), width, height)) {
|
|
1822
|
-
inCard[i] = 1;
|
|
1823
|
-
if (inTone(aligned, i, tone)) toned[i] = 1;
|
|
1824
|
-
}
|
|
1825
|
-
}
|
|
1826
|
-
const near = boxBlur(toned, width, height, GROUND_LINE);
|
|
1827
|
-
return Uint8Array.from(near, (v, i) => v > 0 && inCard[i] ? 1 : 0);
|
|
1828
|
-
}
|
|
1829
|
-
function trustOf(width, height, words) {
|
|
1830
|
-
const trust = new Float32Array(width * height);
|
|
1831
|
-
for (let i = 0; i < trust.length; i++) {
|
|
1832
|
-
const x = i % width;
|
|
1833
|
-
trust[i] = inSpans2(words, (x + 0.5) / width, ((i - x) / width + 0.5) / height) ? 0 : 1;
|
|
1834
|
-
}
|
|
1835
|
-
return trust;
|
|
1836
|
-
}
|
|
1837
|
-
var cutPlanes = remembered();
|
|
1838
|
-
var shrunkCuts = remembered();
|
|
1839
|
-
function planesOf(img, r1) {
|
|
1840
|
-
const own = [0, 1, 2].map((c) => channelOf(img, c));
|
|
1841
|
-
return {
|
|
1842
|
-
own,
|
|
1843
|
-
low: own.map((p) => soften(p, img.width, img.height, r1)),
|
|
1844
|
-
fine: own.map((p) => boxBlur(p, img.width, img.height, 1))
|
|
1845
|
-
};
|
|
1846
|
-
}
|
|
1847
|
-
var LIGHT_CELL = 0.015;
|
|
1848
|
-
var LIGHT_REACH = 3;
|
|
1849
|
-
function cellSums(a, b, open, width, grid) {
|
|
1850
|
-
const sums = new Float32Array(grid.gw * grid.gh * 7);
|
|
1851
|
-
const own = [...a.own, ...b.own];
|
|
1852
|
-
for (let i = 0; i < open.length; i++) {
|
|
1853
|
-
if (!open[i]) continue;
|
|
1854
|
-
const g = cellOf(grid, i, width) * 7;
|
|
1855
|
-
for (let c = 0; c < 6; c++) sums[g + c] = (sums[g + c] ?? 0) + (own[c]?.[i] ?? 0);
|
|
1856
|
-
sums[g + 6] = (sums[g + 6] ?? 0) + 1;
|
|
1857
|
-
}
|
|
1858
|
-
return sums;
|
|
1859
|
-
}
|
|
1860
|
-
function cellRatios(a, b, open, width, grid) {
|
|
1861
|
-
const sums = cellSums(a, b, open, width, grid);
|
|
1862
|
-
const ratio = new Float32Array(grid.gw * grid.gh * 3).fill(Number.NaN);
|
|
1863
|
-
for (let g = 0; g < grid.gw * grid.gh; g++) {
|
|
1864
|
-
const n = sums[g * 7 + 6] ?? 0;
|
|
1865
|
-
if (n < grid.size * grid.size / 4) continue;
|
|
1866
|
-
for (let c = 0; c < 3; c++) {
|
|
1867
|
-
ratio[g * 3 + c] = ((sums[g * 7 + c] ?? 0) / n + 8) / ((sums[g * 7 + 3 + c] ?? 0) / n + 8);
|
|
1868
|
-
}
|
|
1869
|
-
}
|
|
1870
|
-
return ratio;
|
|
1871
|
-
}
|
|
1872
|
-
function medianNear(values, near, c, scratch) {
|
|
1873
|
-
let known = 0;
|
|
1874
|
-
for (const n of near) {
|
|
1875
|
-
const v = values[n * 3 + c] ?? Number.NaN;
|
|
1876
|
-
if (!Number.isNaN(v)) scratch[known++] = v;
|
|
1877
|
-
}
|
|
1878
|
-
if (known === 0) return 1;
|
|
1879
|
-
const sorted = scratch.subarray(0, known).sort();
|
|
1880
|
-
return sorted[Math.floor(known / 2)] ?? 1;
|
|
1881
|
-
}
|
|
1882
|
-
function medianLight(ratio, grid) {
|
|
1883
|
-
const cells = grid.gw * grid.gh;
|
|
1884
|
-
const median = new Float32Array(cells * 3);
|
|
1885
|
-
const scratch = new Float64Array((2 * LIGHT_REACH + 1) ** 2);
|
|
1886
|
-
for (let g = 0; g < cells; g++) {
|
|
1887
|
-
const near = cellsNear(grid, g, LIGHT_REACH);
|
|
1888
|
-
for (let c = 0; c < 3; c++) median[g * 3 + c] = medianNear(ratio, near, c, scratch);
|
|
1889
|
-
}
|
|
1890
|
-
const smooth = new Float32Array(cells * 3);
|
|
1891
|
-
for (let k = 0; k < cells * 3; k++) {
|
|
1892
|
-
const near = cellsNear(grid, Math.floor(k / 3), 1);
|
|
1893
|
-
smooth[k] = near.reduce((sum, n) => sum + (median[n * 3 + k % 3] ?? 1), 0) / near.length;
|
|
1894
|
-
}
|
|
1895
|
-
return smooth;
|
|
1896
|
-
}
|
|
1897
|
-
function lightOf(a, b, open, width, height) {
|
|
1898
|
-
const grid = gridOver(width, height, Math.max(8, Math.round(width * LIGHT_CELL)));
|
|
1899
|
-
const light = medianLight(cellRatios(a, b, open, width, grid), grid);
|
|
1900
|
-
const gain = enlarge(light, { width: grid.gw, height: grid.gh }, { width, height }, 3);
|
|
1901
|
-
const kept = new Float32Array(gain.length);
|
|
1902
|
-
for (let i = 0; i < gain.length; i++) kept[i] = Math.min(GAIN_MAX2, Math.max(1 / GAIN_MAX2, gain[i] ?? 0));
|
|
1903
|
-
return kept;
|
|
1904
|
-
}
|
|
1905
|
-
function compared(a, b, gain, n) {
|
|
1906
|
-
const structure = new Float32Array(n);
|
|
1907
|
-
const detail = new Float32Array(n);
|
|
1908
|
-
for (let c = 0; c < 3; c++) {
|
|
1909
|
-
const [aLow, bLow, aFine, bFine] = [a.low[c], b.low[c], a.fine[c], b.fine[c]];
|
|
1910
|
-
for (let i = 0; i < n; i++) {
|
|
1911
|
-
const g = gain[i * 3 + c] ?? 1;
|
|
1912
|
-
structure[i] = Math.max(structure[i] ?? 0, Math.abs((aLow?.[i] ?? 0) - g * (bLow?.[i] ?? 0)));
|
|
1913
|
-
detail[i] = Math.max(detail[i] ?? 0, Math.abs((aFine?.[i] ?? 0) - g * (bFine?.[i] ?? 0)));
|
|
1914
|
-
}
|
|
1915
|
-
}
|
|
1916
|
-
return { structure, detail };
|
|
1917
|
-
}
|
|
1918
|
-
function renderDiff(aligned, cut, words) {
|
|
1919
|
-
const { width, height } = cut;
|
|
1920
|
-
const r1 = Math.max(2, Math.round(width * STRUCTURE_RADIUS));
|
|
1921
|
-
const a = planesOf(aligned, r1);
|
|
1922
|
-
const b = cutPlanes(cut, String(r1), () => planesOf(cut, r1));
|
|
1923
|
-
const gain = lightOf(a, b, trustOf(width, height, words), width, height);
|
|
1924
|
-
const { structure, detail } = compared(a, b, gain, width * height);
|
|
1925
|
-
const flat = flatOf2(aligned);
|
|
1926
|
-
const surface = surfaceOf(flat, structure, width, height, words);
|
|
1927
|
-
let ground;
|
|
1928
|
-
return {
|
|
1929
|
-
width,
|
|
1930
|
-
height,
|
|
1931
|
-
structure,
|
|
1932
|
-
detail,
|
|
1933
|
-
flat,
|
|
1934
|
-
surface,
|
|
1935
|
-
gain,
|
|
1936
|
-
// Read only to put a subject back: a render refused is never read for it.
|
|
1937
|
-
get ground() {
|
|
1938
|
-
ground ??= groundOf(aligned, flat, words);
|
|
1939
|
-
return ground;
|
|
1940
|
-
}
|
|
1941
|
-
};
|
|
1942
|
-
}
|
|
1943
|
-
function subjectDrift(diff, subject, words) {
|
|
1944
|
-
const { width, height } = diff;
|
|
1945
|
-
const x0 = Math.max(0, Math.floor(subject.x0 * width));
|
|
1946
|
-
const x1 = Math.min(width, Math.ceil(subject.x1 * width));
|
|
1947
|
-
const y0 = Math.max(0, Math.floor(subject.y0 * height));
|
|
1948
|
-
const y1 = Math.min(height, Math.ceil(subject.y1 * height));
|
|
1949
|
-
const n = { all: 0, open: 0, covered: 0, moved: 0, drift: 0, held: 0 };
|
|
1950
|
-
for (let y = y0; y < y1; y++) {
|
|
1951
|
-
for (let x = x0; x < x1; x++) {
|
|
1952
|
-
n.all++;
|
|
1953
|
-
if (!inSpans2(words, (x + 0.5) / width, (y + 0.5) / height)) countDrift(n, diff, y * width + x);
|
|
1954
|
-
}
|
|
1955
|
-
}
|
|
1956
|
-
return {
|
|
1957
|
-
shown: n.all > 0 ? (n.open - n.covered) / n.all : 0,
|
|
1958
|
-
moved: n.open > 0 ? n.moved / n.open : 0,
|
|
1959
|
-
drift: n.held > 0 ? n.drift / n.held : 0
|
|
1960
|
-
};
|
|
1961
|
-
}
|
|
1962
|
-
function countDrift(n, diff, i) {
|
|
1963
|
-
n.open++;
|
|
1964
|
-
if ((diff.structure[i] ?? 0) <= MOVED) {
|
|
1965
|
-
n.drift += diff.detail[i] ?? 0;
|
|
1966
|
-
n.held++;
|
|
1967
|
-
} else if (diff.surface[i]) n.covered++;
|
|
1968
|
-
else n.moved++;
|
|
1969
|
-
}
|
|
1970
|
-
var DRIFTED = 3;
|
|
1971
|
-
var MIN_KEPT = 0.75;
|
|
1972
|
-
var MAX_CARRIED = 5e-3;
|
|
1973
|
-
var REDRAWN = 0.2;
|
|
1974
|
-
var WHOLE_PROBLEMS = ["picture_moved", "subject_redrawn", "words_lost", "words_over_face"];
|
|
1975
|
-
function wholeVerdict(r) {
|
|
1976
|
-
if (r.kept < MIN_KEPT || r.carried > MAX_CARRIED) return { ok: false, problem: "picture_moved", what: [] };
|
|
1977
|
-
const redrawn = r.subjects.filter((s) => s.moved > REDRAWN).map((s) => s.label);
|
|
1978
|
-
if (redrawn.length > 0) return { ok: false, problem: "subject_redrawn", what: redrawn };
|
|
1979
|
-
if (r.lost.length > 0) return { ok: false, problem: "words_lost", what: [...r.lost] };
|
|
1980
|
-
if (r.onFace.length > 0) return { ok: false, problem: "words_over_face", what: [...r.onFace] };
|
|
1981
|
-
return { ok: true, restore: r.subjects.filter((s) => s.drift > DRIFTED).map((s) => s.label) };
|
|
1982
|
-
}
|
|
1983
|
-
function greyOf(img, f) {
|
|
1984
|
-
const width = Math.max(1, Math.floor(img.width / f));
|
|
1985
|
-
const height = Math.max(1, Math.floor(img.height / f));
|
|
1986
|
-
const data = new Float32Array(width * height);
|
|
1987
|
-
for (let i = 0; i < width * height; i++) {
|
|
1988
|
-
const x = i % width * f;
|
|
1989
|
-
const y = Math.floor(i / width) * f;
|
|
1990
|
-
let sum = 0;
|
|
1991
|
-
for (let j = 0; j < f; j++) {
|
|
1992
|
-
for (let k = 0; k < f; k++) {
|
|
1993
|
-
const o = ((y + j) * img.width + x + k) * img.channels;
|
|
1994
|
-
sum += 0.299 * (img.data[o] ?? 0) + 0.587 * (img.data[o + 1] ?? 0) + 0.114 * (img.data[o + 2] ?? 0);
|
|
1995
|
-
}
|
|
1996
|
-
}
|
|
1997
|
-
data[i] = sum / (f * f);
|
|
1998
|
-
}
|
|
1999
|
-
return { data, width, height };
|
|
2000
|
-
}
|
|
2001
|
-
function likeness(render, cut, box, place) {
|
|
2002
|
-
const cx = box.x + box.width / 2;
|
|
2003
|
-
const cy = box.y + box.height / 2;
|
|
2004
|
-
const n = { count: 0, a: 0, c: 0, aa: 0, cc: 0, ac: 0 };
|
|
2005
|
-
for (let y = box.y; y < box.y + box.height; y++) {
|
|
2006
|
-
for (let x = box.x; x < box.x + box.width; x++) {
|
|
2007
|
-
const u = Math.round(cx + place.scale * (x - cx) + place.dx);
|
|
2008
|
-
const v = Math.round(cy + place.scale * (y - cy) + place.dy);
|
|
2009
|
-
if (u < 0 || v < 0 || u >= render.width || v >= render.height) return -1;
|
|
2010
|
-
const a = render.data[v * render.width + u] ?? 0;
|
|
2011
|
-
const c = cut.data[y * cut.width + x] ?? 0;
|
|
2012
|
-
n.count++;
|
|
2013
|
-
n.a += a;
|
|
2014
|
-
n.c += c;
|
|
2015
|
-
n.aa += a * a;
|
|
2016
|
-
n.cc += c * c;
|
|
2017
|
-
n.ac += a * c;
|
|
2018
|
-
}
|
|
2019
|
-
}
|
|
2020
|
-
const va = n.aa - n.a * n.a / n.count;
|
|
2021
|
-
const vc = n.cc - n.c * n.c / n.count;
|
|
2022
|
-
return va > 1e-6 && vc > 1e-6 ? (n.ac - n.a * n.c / n.count) / Math.sqrt(va * vc) : -1;
|
|
2023
|
-
}
|
|
2024
|
-
function boxIn(s, g, inset) {
|
|
2025
|
-
const w = (s.x1 - s.x0) * g.width;
|
|
2026
|
-
const h = (s.y1 - s.y0) * g.height;
|
|
2027
|
-
const x = Math.max(0, Math.round(s.x0 * g.width + inset * w));
|
|
2028
|
-
const y = Math.max(0, Math.round(s.y0 * g.height + inset * h));
|
|
2029
|
-
return {
|
|
2030
|
-
x,
|
|
2031
|
-
y,
|
|
2032
|
-
width: Math.max(1, Math.min(g.width - x, Math.round(w * (1 - 2 * inset)))),
|
|
2033
|
-
height: Math.max(1, Math.min(g.height - y, Math.round(h * (1 - 2 * inset))))
|
|
2034
|
-
};
|
|
2035
|
-
}
|
|
2036
|
-
function bestPlace(render, cut, box, places) {
|
|
2037
|
-
let best = { dx: 0, dy: 0, scale: 1, score: -2 };
|
|
2038
|
-
for (const place of places) {
|
|
2039
|
-
const score = likeness(render, cut, box, place);
|
|
2040
|
-
if (score > best.score) best = { ...place, score };
|
|
2041
|
-
}
|
|
2042
|
-
return best;
|
|
2043
|
-
}
|
|
2044
|
-
var TRACK_REACH = 0.25;
|
|
2045
|
-
var TRACK_SCALES = [0.85, 0.92, 1, 1.08, 1.15];
|
|
2046
|
-
var TRACK_WIDTH = 180;
|
|
2047
|
-
function* placesNear(around, reach, scales) {
|
|
2048
|
-
for (const scale of scales) {
|
|
2049
|
-
for (let dy = -reach; dy <= reach; dy++) {
|
|
2050
|
-
for (let dx = -reach; dx <= reach; dx++) yield { dx: around.dx + dx, dy: around.dy + dy, scale };
|
|
2051
|
-
}
|
|
2052
|
-
}
|
|
2053
|
-
}
|
|
2054
|
-
function tracked(aligned, cut, subject) {
|
|
2055
|
-
const f = Math.max(1, Math.round(cut.width / TRACK_WIDTH));
|
|
2056
|
-
const coarse = { render: greyOf(aligned, f), cut: greyOf(cut, f) };
|
|
2057
|
-
const reach = Math.round(TRACK_REACH * coarse.cut.width);
|
|
2058
|
-
const found = bestPlace(
|
|
2059
|
-
coarse.render,
|
|
2060
|
-
coarse.cut,
|
|
2061
|
-
boxIn(subject, coarse.cut, 0.1),
|
|
2062
|
-
placesNear({ dx: 0, dy: 0, scale: 1 }, reach, TRACK_SCALES)
|
|
2063
|
-
);
|
|
2064
|
-
const fine = { render: greyOf(aligned, 1), cut: greyOf(cut, 1) };
|
|
2065
|
-
const near = [found.scale - 0.03, found.scale, found.scale + 0.03];
|
|
2066
|
-
const refined = bestPlace(
|
|
2067
|
-
fine.render,
|
|
2068
|
-
fine.cut,
|
|
2069
|
-
boxIn(subject, fine.cut, 0.1),
|
|
2070
|
-
placesNear({ dx: found.dx * f, dy: found.dy * f, scale: 1 }, f, near)
|
|
2071
|
-
);
|
|
2072
|
-
return {
|
|
2073
|
-
dx: refined.dx / cut.width,
|
|
2074
|
-
dy: refined.dy / cut.height,
|
|
2075
|
-
scale: refined.scale,
|
|
2076
|
-
score: refined.score
|
|
2077
|
-
};
|
|
2078
|
-
}
|
|
2079
|
-
var FACE_EDGE = 0.15;
|
|
2080
|
-
function wordsOnFaces(words, faces) {
|
|
2081
|
-
return facesUnder(words, faces).map((f) => f.label);
|
|
2082
|
-
}
|
|
2083
|
-
function facesUnder(words, faces) {
|
|
2084
|
-
return faces.filter((f) => {
|
|
2085
|
-
const dx = FACE_EDGE * (f.x1 - f.x0);
|
|
2086
|
-
const dy = FACE_EDGE * (f.y1 - f.y0);
|
|
2087
|
-
return words.some((w) => w.x0 < f.x1 - dx && w.x1 > f.x0 + dx && w.y0 < f.y1 - dy && w.y1 > f.y0 + dy);
|
|
2088
|
-
});
|
|
2089
|
-
}
|
|
2090
|
-
var FRAME_MARGIN = 0.01;
|
|
2091
|
-
function wordsLost(words, format, placements, keepOut = []) {
|
|
2092
|
-
const cut = words.filter(
|
|
2093
|
-
(b) => b.x0 < FRAME_MARGIN || b.y0 < FRAME_MARGIN || b.x1 > 1 - FRAME_MARGIN || b.y1 > 1 - FRAME_MARGIN || keepOut.some((k) => overlaps2(b, k))
|
|
2094
|
-
);
|
|
2095
|
-
const covered = bandViolations(
|
|
2096
|
-
words.filter((b) => !cut.includes(b)),
|
|
2097
|
-
format,
|
|
2098
|
-
{ placements: blockingPlacements(placements) }
|
|
2099
|
-
).filter((v) => v.covered);
|
|
2100
|
-
return [...cut.map((b) => b.text), ...covered.map((v) => v.text)];
|
|
2101
|
-
}
|
|
2102
|
-
var ZONE_MARGIN = 0.12;
|
|
2103
|
-
var FEATHER = 4e-3;
|
|
2104
|
-
function addZone(into2, zone, width, height) {
|
|
2105
|
-
const x0 = zone.x0 * width;
|
|
2106
|
-
const x1 = zone.x1 * width;
|
|
2107
|
-
const y0 = zone.y0 * height;
|
|
2108
|
-
const y1 = zone.y1 * height;
|
|
2109
|
-
const margin = Math.max(4, ZONE_MARGIN * Math.max(x1 - x0, y1 - y0));
|
|
2110
|
-
for (let y = Math.max(0, Math.floor(y0 - margin)); y < Math.min(height, Math.ceil(y1 + margin)); y++) {
|
|
2111
|
-
for (let x = Math.max(0, Math.floor(x0 - margin)); x < Math.min(width, Math.ceil(x1 + margin)); x++) {
|
|
2112
|
-
const dx = Math.max(0, x0 - (x + 0.5), x + 0.5 - x1);
|
|
2113
|
-
const dy = Math.max(0, y0 - (y + 0.5), y + 0.5 - y1);
|
|
2114
|
-
const t = 1 - Math.min(1, Math.sqrt(dx * dx + dy * dy) / margin);
|
|
2115
|
-
const i = y * width + x;
|
|
2116
|
-
into2[i] = Math.max(into2[i] ?? 0, t * t * (3 - 2 * t));
|
|
2117
|
-
}
|
|
2118
|
-
}
|
|
2119
|
-
}
|
|
2120
|
-
function restoreMask(diff, zones, words) {
|
|
2121
|
-
const { width, height } = diff;
|
|
2122
|
-
const zone = new Float32Array(width * height);
|
|
2123
|
-
for (const z7 of zones) addZone(zone, z7, width, height);
|
|
2124
|
-
const laid = boxBlur(
|
|
2125
|
-
Float32Array.from(diff.flat, (f, i) => f && (diff.detail[i] ?? 0) > KEPT_DETAIL || diff.ground[i] ? 1 : 0),
|
|
2126
|
-
width,
|
|
2127
|
-
height,
|
|
2128
|
-
1
|
|
2129
|
-
);
|
|
2130
|
-
const off = trustOf(width, height, words).map((t, i) => t > 0 && (laid[i] ?? 0) === 0 ? 0 : 1);
|
|
2131
|
-
const held = zone.map(
|
|
2132
|
-
(z7, i) => z7 > 0 && !off[i] ? z7 * Math.min(1, Math.max(0, (MOVED - (diff.structure[i] ?? 0)) / (MOVED - STILL))) : 0
|
|
2133
|
-
);
|
|
2134
|
-
const feathered = soften(held, width, height, Math.max(2, Math.round(width * FEATHER)));
|
|
2135
|
-
return feathered.map((m, i) => off[i] ? 0 : m);
|
|
2136
|
-
}
|
|
2137
|
-
function restoreSubjects(aligned, cut, gain, mask) {
|
|
2138
|
-
const { width, height } = cut;
|
|
2139
|
-
const out = Buffer.alloc(width * height * 3);
|
|
2140
|
-
for (let i = 0; i < width * height; i++) {
|
|
2141
|
-
const m = mask[i] ?? 0;
|
|
2142
|
-
for (let c = 0; c < 3; c++) {
|
|
2143
|
-
const own = aligned.data[i * aligned.channels + c] ?? 0;
|
|
2144
|
-
if (m <= 0) {
|
|
2145
|
-
out[i * 3 + c] = own;
|
|
2146
|
-
continue;
|
|
2147
|
-
}
|
|
2148
|
-
const back = Math.min(255, (cut.data[i * cut.channels + c] ?? 0) * (gain[i * 3 + c] ?? 1));
|
|
2149
|
-
out[i * 3 + c] = Math.round(own * (1 - m) + back * m);
|
|
2150
|
-
}
|
|
2151
|
-
}
|
|
2152
|
-
return { data: out, width, height, channels: 3 };
|
|
2153
|
-
}
|
|
2154
|
-
var KEPT_DETAIL = 24;
|
|
2155
|
-
function keptShare(diff, words) {
|
|
2156
|
-
const { width, height } = diff;
|
|
2157
|
-
let textured = 0;
|
|
2158
|
-
let kept = 0;
|
|
2159
|
-
for (let i = 0; i < width * height; i++) {
|
|
2160
|
-
if (diff.flat[i] || diff.surface[i]) continue;
|
|
2161
|
-
const x = i % width;
|
|
2162
|
-
if (inSpans2(words, (x + 0.5) / width, ((i - x) / width + 0.5) / height)) continue;
|
|
2163
|
-
textured++;
|
|
2164
|
-
if ((diff.detail[i] ?? 0) < KEPT_DETAIL) kept++;
|
|
2165
|
-
}
|
|
2166
|
-
return textured > 0 ? kept / textured : 1;
|
|
2167
|
-
}
|
|
2168
|
-
function cropRaw(img, rect) {
|
|
2169
|
-
const data = Buffer.alloc(rect.width * rect.height * 3);
|
|
2170
|
-
for (let y = 0; y < rect.height; y++) {
|
|
2171
|
-
const from = ((rect.y + y) * img.width + rect.x) * img.channels;
|
|
2172
|
-
if (img.channels === 3) {
|
|
2173
|
-
img.data.copy(data, y * rect.width * 3, from, from + rect.width * 3);
|
|
2174
|
-
continue;
|
|
2175
|
-
}
|
|
2176
|
-
for (let x = 0; x < rect.width; x++) {
|
|
2177
|
-
for (let c = 0; c < 3; c++) data[(y * rect.width + x) * 3 + c] = img.data[from + x * img.channels + c] ?? 0;
|
|
2178
|
-
}
|
|
2179
|
-
}
|
|
2180
|
-
return { data, width: rect.width, height: rect.height, channels: 3 };
|
|
2181
|
-
}
|
|
2182
|
-
function carriedBy(fit, frame, width, height) {
|
|
2183
|
-
const at = (x, y) => [
|
|
2184
|
-
width / 2 + fit.scale * (x - width / 2) + fit.dx,
|
|
2185
|
-
height / 2 + fit.scale * (y - height / 2) + fit.dy
|
|
2186
|
-
];
|
|
2187
|
-
const [left = 0, top = 0] = at(frame.x, frame.y);
|
|
2188
|
-
const [right = 0, bottom = 0] = at(frame.x + frame.width, frame.y + frame.height);
|
|
2189
|
-
return Math.max(
|
|
2190
|
-
0,
|
|
2191
|
-
-left / frame.width,
|
|
2192
|
-
-top / frame.height,
|
|
2193
|
-
(right - width) / frame.width,
|
|
2194
|
-
(bottom - height) / frame.height
|
|
2195
|
-
);
|
|
2196
|
-
}
|
|
2197
|
-
var SHOWN_MIN = 0.5;
|
|
2198
|
-
var JUDGED_WIDTH = 720;
|
|
2199
|
-
function shrinkRaw(img, f) {
|
|
2200
|
-
const width = Math.floor(img.width / f);
|
|
2201
|
-
const height = Math.floor(img.height / f);
|
|
2202
|
-
const data = Buffer.alloc(width * height * 3);
|
|
2203
|
-
for (let i = 0; i < width * height; i++) {
|
|
2204
|
-
const x = i % width * f;
|
|
2205
|
-
const y = Math.floor(i / width) * f;
|
|
2206
|
-
for (let c = 0; c < 3; c++) {
|
|
2207
|
-
let sum = 0;
|
|
2208
|
-
for (let j = 0; j < f; j++) {
|
|
2209
|
-
const row = ((y + j) * img.width + x) * img.channels + c;
|
|
2210
|
-
for (let k = 0; k < f; k++) sum += img.data[row + k * img.channels] ?? 0;
|
|
2211
|
-
}
|
|
2212
|
-
data[i * 3 + c] = Math.round(sum / (f * f));
|
|
2213
|
-
}
|
|
2214
|
-
}
|
|
2215
|
-
return { data, width, height, channels: 3 };
|
|
2216
|
-
}
|
|
2217
|
-
function axisTaps(from, to) {
|
|
2218
|
-
const near = new Int32Array(to);
|
|
2219
|
-
const far = new Int32Array(to);
|
|
2220
|
-
const t = new Float32Array(to);
|
|
2221
|
-
for (let k = 0; k < to; k++) {
|
|
2222
|
-
const u = Math.min(from - 1, Math.max(0, (k + 0.5) * from / to - 0.5));
|
|
2223
|
-
near[k] = Math.floor(u);
|
|
2224
|
-
far[k] = Math.min(from - 1, Math.floor(u) + 1);
|
|
2225
|
-
t[k] = u - Math.floor(u);
|
|
2226
|
-
}
|
|
2227
|
-
return { near, far, t };
|
|
2228
|
-
}
|
|
2229
|
-
function enlargeRow(values, out, xs, row, stride) {
|
|
2230
|
-
const { top, bottom, fy } = row;
|
|
2231
|
-
for (let x = 0; x < row.width; x++) {
|
|
2232
|
-
const left = xs.near[x] ?? 0;
|
|
2233
|
-
const right = xs.far[x] ?? 0;
|
|
2234
|
-
const fx = xs.t[x] ?? 0;
|
|
2235
|
-
const o = (row.y * row.width + x) * stride;
|
|
2236
|
-
for (let c = 0; c < stride; c++) {
|
|
2237
|
-
const upper = (values[(top + left) * stride + c] ?? 0) * (1 - fx) + (values[(top + right) * stride + c] ?? 0) * fx;
|
|
2238
|
-
const lower = (values[(bottom + left) * stride + c] ?? 0) * (1 - fx) + (values[(bottom + right) * stride + c] ?? 0) * fx;
|
|
2239
|
-
out[o + c] = upper * (1 - fy) + lower * fy;
|
|
2240
|
-
}
|
|
2241
|
-
}
|
|
2242
|
-
}
|
|
2243
|
-
function enlarge(values, from, to, stride) {
|
|
2244
|
-
if (from.width === to.width && from.height === to.height) return values;
|
|
2245
|
-
const xs = axisTaps(from.width, to.width);
|
|
2246
|
-
const ys = axisTaps(from.height, to.height);
|
|
2247
|
-
const out = new Float32Array(to.width * to.height * stride);
|
|
2248
|
-
for (let y = 0; y < to.height; y++) {
|
|
2249
|
-
const top = (ys.near[y] ?? 0) * from.width;
|
|
2250
|
-
const bottom = (ys.far[y] ?? 0) * from.width;
|
|
2251
|
-
enlargeRow(values, out, xs, { y, top, bottom, fy: ys.t[y] ?? 0, width: to.width }, stride);
|
|
2252
|
-
}
|
|
2253
|
-
return out;
|
|
2254
|
-
}
|
|
2255
|
-
function moveOnRender(m, frame, width, height) {
|
|
2256
|
-
const x = (v) => (frame.x + v * frame.width) / width;
|
|
2257
|
-
const y = (v) => (frame.y + v * frame.height) / height;
|
|
2258
|
-
return {
|
|
2259
|
-
group: { x0: x(m.group.x0), y0: y(m.group.y0), x1: x(m.group.x1), y1: y(m.group.y1) },
|
|
2260
|
-
fit: {
|
|
2261
|
-
scale: m.fit.scale,
|
|
2262
|
-
cx: x(m.fit.cx),
|
|
2263
|
-
cy: y(m.fit.cy),
|
|
2264
|
-
dx: m.fit.dx * frame.width / width,
|
|
2265
|
-
dy: m.fit.dy * frame.height / height
|
|
2266
|
-
}
|
|
2267
|
-
};
|
|
2268
|
-
}
|
|
2269
|
-
var overlaps2 = (a, b) => a.x0 < b.x1 && a.x1 > b.x0 && a.y0 < b.y1 && a.y1 > b.y0;
|
|
2270
|
-
var shifted = (box, dx, dy) => ({
|
|
2271
|
-
...box,
|
|
2272
|
-
x0: box.x0 + dx,
|
|
2273
|
-
y0: box.y0 + dy,
|
|
2274
|
-
x1: box.x1 + dx,
|
|
2275
|
-
y1: box.y1 + dy
|
|
2276
|
-
});
|
|
2277
|
-
var moveOf = (box, moves) => moves.find((m) => inSpans2([m.group], (box.x0 + box.x1) / 2, (box.y0 + box.y1) / 2));
|
|
2278
|
-
var hull = (a, b) => ({
|
|
2279
|
-
x0: Math.min(a.x0, b.x0),
|
|
2280
|
-
y0: Math.min(a.y0, b.y0),
|
|
2281
|
-
x1: Math.max(a.x1, b.x1),
|
|
2282
|
-
y1: Math.max(a.y1, b.y1)
|
|
2283
|
-
});
|
|
2284
|
-
var BARE = 0.02;
|
|
2285
|
-
var frameSpan = (frame, width, height) => ({
|
|
2286
|
-
x0: frame.x / width,
|
|
2287
|
-
y0: frame.y / height,
|
|
2288
|
-
x1: (frame.x + frame.width) / width,
|
|
2289
|
-
y1: (frame.y + frame.height) / height
|
|
2290
|
-
});
|
|
2291
|
-
var CARD_CUT = 5e-3;
|
|
2292
|
-
function cardCut(band, f) {
|
|
2293
|
-
const w = f.x1 - f.x0;
|
|
2294
|
-
const h = f.y1 - f.y0;
|
|
2295
|
-
const across2 = band.x0 <= f.x0 + FRAME_MARGIN * w && band.x1 >= f.x1 - FRAME_MARGIN * w;
|
|
2296
|
-
const down = band.y0 <= f.y0 + FRAME_MARGIN * h && band.y1 >= f.y1 - FRAME_MARGIN * h;
|
|
2297
|
-
const pastY = band.y0 < f.y0 - CARD_CUT * h || band.y1 > f.y1 + CARD_CUT * h;
|
|
2298
|
-
const pastX = band.x0 < f.x0 - CARD_CUT * w || band.x1 > f.x1 + CARD_CUT * w;
|
|
2299
|
-
return pastY && !across2 || pastX && !down;
|
|
2300
|
-
}
|
|
2301
|
-
function cutCardOf(laid, line, f) {
|
|
2302
|
-
const band = bandUnder(laid, line);
|
|
2303
|
-
return band && cardCut(band, f) ? band : null;
|
|
2304
|
-
}
|
|
2305
|
-
var unique = (boxes) => boxes.filter(
|
|
2306
|
-
(b, i) => boxes.findIndex((o) => o.x0 === b.x0 && o.y0 === b.y0 && o.x1 === b.x1 && o.y1 === b.y1) === i
|
|
2307
|
-
);
|
|
2308
|
-
function merged(spans) {
|
|
2309
|
-
const out = [];
|
|
2310
|
-
for (const s of spans) {
|
|
2311
|
-
let joined = s;
|
|
2312
|
-
for (let i = out.length - 1; i >= 0; i--) {
|
|
2313
|
-
const o = out[i];
|
|
2314
|
-
if (o && overlaps2(o, joined)) {
|
|
2315
|
-
joined = hull(o, joined);
|
|
2316
|
-
out.splice(i, 1);
|
|
2317
|
-
}
|
|
2318
|
-
}
|
|
2319
|
-
out.push(joined);
|
|
2320
|
-
}
|
|
2321
|
-
return out;
|
|
2322
|
-
}
|
|
2323
|
-
function carriersOf(m, laid, designed) {
|
|
2324
|
-
const lines = designed.filter((b) => inSpans2([m.group], (b.x0 + b.x1) / 2, (b.y0 + b.y1) / 2));
|
|
2325
|
-
const bands = lines.flatMap((b) => {
|
|
2326
|
-
const band = bandUnder(laid, b);
|
|
2327
|
-
return band ? [band] : [];
|
|
2328
|
-
});
|
|
2329
|
-
if (bands.length === 0) return "picture";
|
|
2330
|
-
return bands.length < lines.length ? "mixed" : merged(bands);
|
|
2331
|
-
}
|
|
2332
|
-
function bandMoves(banded, moves, p, at) {
|
|
2333
|
-
const faces = p.subjects.filter((s) => s.kind === "face");
|
|
2334
|
-
const out = [];
|
|
2335
|
-
for (const { move: m, band } of banded) {
|
|
2336
|
-
if (m.fit.scale < 1 - 1e-9 || out.some((o) => overlaps2(o.band, band))) return null;
|
|
2337
|
-
const { dx, dy } = m.fit;
|
|
2338
|
-
const landing = shifted(band, dx, dy);
|
|
2339
|
-
const carried = p.words.designed.filter((b) => inSpans2([band], (b.x0 + b.x1) / 2, (b.y0 + b.y1) / 2));
|
|
2340
|
-
const others = p.words.designed.filter((b) => !carried.includes(b));
|
|
2341
|
-
const ends = (b) => {
|
|
2342
|
-
const own = moveOf(b, moves);
|
|
2343
|
-
return own ? movedSpan(at.extent(b), own.fit) : at.extent(b);
|
|
2344
|
-
};
|
|
2345
|
-
const lost = wordsLost(
|
|
2346
|
-
carried.map((b) => at.inFrame(shifted(b, dx, dy))),
|
|
2347
|
-
p.format,
|
|
2348
|
-
p.placements
|
|
2349
|
-
);
|
|
2350
|
-
const crossed = carried.some((b) => (moveOf(b, moves) ?? m) !== m) || others.some((b) => overlaps2(ends(b), hull(band, landing)));
|
|
2351
|
-
if (lost.length > 0 || crossed || facesUnder([landing], faces).length > 0) return null;
|
|
2352
|
-
out.push({ band, dx, dy, words: carried });
|
|
2353
|
-
}
|
|
2354
|
-
return out;
|
|
2355
|
-
}
|
|
2356
|
-
function wordsMoved(p, out, moves) {
|
|
2357
|
-
const { layer } = wordsLayer(p.aligned, p.cut, p.words.designed, {
|
|
2358
|
-
subjects: p.subjects,
|
|
2359
|
-
keepOut: p.words.inScene
|
|
2360
|
-
});
|
|
2361
|
-
const part = movedPart(layer, moves);
|
|
2362
|
-
const left = soften(
|
|
2363
|
-
part.alpha.map((a) => a > 0 ? 1 : 0),
|
|
2364
|
-
p.cut.width,
|
|
2365
|
-
p.cut.height,
|
|
2366
|
-
2
|
|
2367
|
-
).map((a, i) => Math.max(a, part.alpha[i] ?? 0));
|
|
2368
|
-
return compose(restoreSubjects(out, p.cut, p.gain, left), moveLayer(part, moves));
|
|
2369
|
-
}
|
|
2370
|
-
function placeWords(p) {
|
|
2371
|
-
const { width, height } = p.cut;
|
|
2372
|
-
const inFrame = (b) => ({
|
|
2373
|
-
...b,
|
|
2374
|
-
x0: (b.x0 * width - p.frame.x) / p.frame.width,
|
|
2375
|
-
x1: (b.x1 * width - p.frame.x) / p.frame.width,
|
|
2376
|
-
y0: (b.y0 * height - p.frame.y) / p.frame.height,
|
|
2377
|
-
y1: (b.y1 * height - p.frame.y) / p.frame.height
|
|
2378
|
-
});
|
|
2379
|
-
const showing = (b) => b.x1 > 0 && b.y1 > 0 && b.x0 < 1 && b.y0 < 1;
|
|
2380
|
-
const laid = laidMap(p.judged.aligned, p.judged.cut);
|
|
2381
|
-
const f = frameSpan(p.frame, width, height);
|
|
2382
|
-
const placed2 = p.words.designed.map((b) => {
|
|
2383
|
-
const card = cutCardOf(laid, b, f);
|
|
2384
|
-
return card ? { ...b, ...card } : b;
|
|
2385
|
-
});
|
|
2386
|
-
const plan = planWordsShift(unique(placed2).map(inFrame).filter(showing), p.format, {
|
|
2387
|
-
...p.placements ? { placements: p.placements } : {},
|
|
2388
|
-
...p.minScale !== void 0 ? { minScale: p.minScale } : {},
|
|
2389
|
-
faces: p.subjects.filter((s) => s.kind === "face").map(inFrame).filter(showing),
|
|
2390
|
-
// Never into the logo's slot, nor out of it: a block moved out of it had its band cut through the letters.
|
|
2391
|
-
...p.keepOut ? { keepOut: p.keepOut } : {}
|
|
2392
|
-
});
|
|
2393
|
-
if (plan.reason || plan.moves.length === 0) return null;
|
|
2394
|
-
const moves = plan.moves.map((m) => moveOnRender(m, p.frame, width, height));
|
|
2395
|
-
const under = moves.map((move) => ({ move, on: carriersOf(move, laid, p.words.designed) }));
|
|
2396
|
-
const unsure = (u) => u.on === "mixed" || u.on === "picture" && laidAround(laid, u.move.group) > BARE;
|
|
2397
|
-
if (under.some(unsure)) return null;
|
|
2398
|
-
const banded = under.flatMap((u) => Array.isArray(u.on) ? u.on.map((band) => ({ move: u.move, band })) : []);
|
|
2399
|
-
const bands = bandMoves(banded, moves, p, { inFrame, extent: (b) => bandUnder(laid, b) ?? b });
|
|
2400
|
-
if (!bands) return null;
|
|
2401
|
-
const loose = under.flatMap((u) => u.on === "picture" ? [u.move] : []);
|
|
2402
|
-
let out = loose.length > 0 ? wordsMoved(p, p.out, loose) : p.out;
|
|
2403
|
-
for (const band of bands) out = moveBand({ aligned: p.aligned, cut: p.cut, out }, band);
|
|
2404
|
-
return { out, moves: plan.moves };
|
|
2405
|
-
}
|
|
2406
|
-
function takeWhole(input) {
|
|
2407
|
-
const { render, cut, frame } = input;
|
|
2408
|
-
const fit = registerToSource(render, cut, input.words?.designed ?? []);
|
|
2409
|
-
const aligned = warpToCut(render, fit);
|
|
2410
|
-
if (!input.words) {
|
|
2411
|
-
const out2 = cropRaw(aligned, frame);
|
|
2412
|
-
return { ok: true, out: out2, restored: [], covered: [], moves: [], kept: 1, residual: fit.residual };
|
|
2413
|
-
}
|
|
2414
|
-
const judging = judge(input, input.words, fit, aligned);
|
|
2415
|
-
const { verdict, kept } = judging;
|
|
2416
|
-
const misplaced = !verdict.ok && (verdict.problem === "words_lost" || verdict.problem === "words_over_face");
|
|
2417
|
-
if (!verdict.ok && !misplaced) return { ...verdict, kept, residual: fit.residual };
|
|
2418
|
-
const { out, gain, taken } = putBack(input, input.words, aligned, judging);
|
|
2419
|
-
const done = { ...taken, kept, residual: fit.residual };
|
|
2420
|
-
if (verdict.ok) return { ok: true, out: cropRaw(out, frame), moves: [], ...done };
|
|
2421
|
-
const placed2 = placeWords({
|
|
2422
|
-
aligned,
|
|
2423
|
-
cut,
|
|
2424
|
-
out,
|
|
2425
|
-
gain: gain(),
|
|
2426
|
-
judged: judging.judged,
|
|
2427
|
-
frame,
|
|
2428
|
-
words: input.words,
|
|
2429
|
-
subjects: input.subjects,
|
|
2430
|
-
format: input.format,
|
|
2431
|
-
...input.placements ? { placements: input.placements } : {},
|
|
2432
|
-
...input.minScale !== void 0 ? { minScale: input.minScale } : {},
|
|
2433
|
-
...input.keepOut ? { keepOut: input.keepOut } : {}
|
|
2434
|
-
});
|
|
2435
|
-
if (!placed2) return { ...verdict, kept, residual: fit.residual, whole: { out: cropRaw(out, frame), ...taken } };
|
|
2436
|
-
return { ok: true, out: cropRaw(placed2.out, frame), moves: placed2.moves, ...done };
|
|
2437
|
-
}
|
|
2438
|
-
function judge(input, read, fit, aligned) {
|
|
2439
|
-
const { cut, frame } = input;
|
|
2440
|
-
const { width, height } = cut;
|
|
2441
|
-
const words = read.designed.map((b) => padded(b, width, height));
|
|
2442
|
-
const shrink2 = Math.max(1, Math.round(width / JUDGED_WIDTH));
|
|
2443
|
-
const judged = shrink2 > 1 ? { aligned: shrinkRaw(aligned, shrink2), cut: shrunkCuts(cut, String(shrink2), () => shrinkRaw(cut, shrink2)) } : { aligned, cut };
|
|
2444
|
-
const diff = renderDiff(judged.aligned, judged.cut, words);
|
|
2445
|
-
const drifts = input.subjects.map((s) => ({ subject: s, ...subjectDrift(diff, s, words) }));
|
|
2446
|
-
const inFrame = (b) => ({
|
|
2447
|
-
...b,
|
|
2448
|
-
x0: (b.x0 * width - frame.x) / frame.width,
|
|
2449
|
-
x1: (b.x1 * width - frame.x) / frame.width,
|
|
2450
|
-
y0: (b.y0 * height - frame.y) / frame.height,
|
|
2451
|
-
y1: (b.y1 * height - frame.y) / frame.height
|
|
2452
|
-
});
|
|
2453
|
-
const kept = keptShare(diff, words);
|
|
2454
|
-
const gone = /* @__PURE__ */ new Set();
|
|
2455
|
-
const laid = laidMap(judged.aligned, judged.cut);
|
|
2456
|
-
const f = frameSpan(frame, width, height);
|
|
2457
|
-
const carded = read.designed.filter((b) => cutCardOf(laid, b, f)).map((b) => b.text);
|
|
2458
|
-
const lost = wordsLost(read.designed.map(inFrame), input.format, input.placements, input.keepOut);
|
|
2459
|
-
const base = {
|
|
2460
|
-
kept,
|
|
2461
|
-
carried: carriedBy(fit, frame, width, height),
|
|
2462
|
-
lost: [...lost, ...carded.filter((t) => !lost.includes(t))]
|
|
2463
|
-
};
|
|
2464
|
-
const faces = () => input.subjects.filter((s) => s.kind === "face" && !gone.has(s));
|
|
2465
|
-
const readingNow = () => ({
|
|
2466
|
-
...base,
|
|
2467
|
-
subjects: drifts.filter((d) => !gone.has(d.subject)).map((d) => ({ label: d.subject.label, moved: d.moved, drift: d.drift, shown: d.shown })),
|
|
2468
|
-
onFace: wordsOnFaces(read.designed, faces())
|
|
2469
|
-
});
|
|
2470
|
-
let verdict = wholeVerdict(readingNow());
|
|
2471
|
-
const relocated = [];
|
|
2472
|
-
for (let round = 0; round < 2 && !verdict.ok && FOLLOWED.has(verdict.problem); round++) {
|
|
2473
|
-
const suspects = verdict.problem === "subject_redrawn" ? drifts.filter((d) => !gone.has(d.subject) && d.moved > REDRAWN).map((d) => d.subject) : facesUnder(read.designed, faces());
|
|
2474
|
-
const found = relocations(withTheirPeople(suspects, input.subjects), judged);
|
|
2475
|
-
if (!found) break;
|
|
2476
|
-
relocated.push(...found);
|
|
2477
|
-
for (const r of found) gone.add(r.subject);
|
|
2478
|
-
verdict = wholeVerdict(readingNow());
|
|
2479
|
-
}
|
|
2480
|
-
return { verdict, reading: readingNow(), kept, diff, judged, words, drifts, relocated };
|
|
2481
|
-
}
|
|
2482
|
-
function withTheirPeople(suspects, subjects) {
|
|
2483
|
-
const holds2 = (outer, inner) => outer.x0 <= inner.x0 + 0.01 && outer.y0 <= inner.y0 + 0.01 && outer.x1 >= inner.x1 - 0.01 && outer.y1 >= inner.y1 - 0.01;
|
|
2484
|
-
const people = subjects.filter(
|
|
2485
|
-
(p) => p.kind === "person" && suspects.some((f) => f.kind === "face" && f.label === p.label && holds2(p, f))
|
|
2486
|
-
);
|
|
2487
|
-
return [.../* @__PURE__ */ new Set([...suspects, ...people])];
|
|
2488
|
-
}
|
|
2489
|
-
var FOLLOWED = /* @__PURE__ */ new Set(["subject_redrawn", "words_over_face"]);
|
|
2490
|
-
function putBack(input, read, aligned, j) {
|
|
2491
|
-
const { cut } = input;
|
|
2492
|
-
const full = { width: cut.width, height: cut.height };
|
|
2493
|
-
const settled = j.verdict.ok ? j.verdict : wholeVerdict({ ...j.reading, lost: [], onFace: [] });
|
|
2494
|
-
const restored = settled.ok ? settled.restore : [];
|
|
2495
|
-
const back = input.subjects.filter((s) => restored.includes(s.label));
|
|
2496
|
-
const zones = [...back, ...read.inScene.map((b) => padded(b, full.width, full.height))];
|
|
2497
|
-
const covered = j.drifts.filter((d) => (d.subject.kind === "product" || d.subject.kind === "animal") && d.shown < SHOWN_MIN).map((d) => d.subject.label);
|
|
2498
|
-
let enlarged;
|
|
2499
|
-
const gain = () => {
|
|
2500
|
-
enlarged ??= enlarge(j.diff.gain, j.diff, full, 3);
|
|
2501
|
-
return enlarged;
|
|
2502
|
-
};
|
|
2503
|
-
let out = zones.length === 0 ? aligned : restoreSubjects(aligned, cut, gain(), enlarge(restoreMask(j.diff, zones, j.words), j.diff, full, 1));
|
|
2504
|
-
for (const r of j.relocated) out = putMoved(out, cut, r, j);
|
|
2505
|
-
const moved = j.relocated.map((r) => r.subject.label);
|
|
2506
|
-
return { out, gain, taken: { restored: [.../* @__PURE__ */ new Set([...restored, ...moved])], covered } };
|
|
2507
|
-
}
|
|
2508
|
-
var TRACK_MAX_AREA = 0.06;
|
|
2509
|
-
var RELOCATED = 0.8;
|
|
2510
|
-
var STAYED = 5e-3;
|
|
2511
|
-
function relocations(subjects, judged) {
|
|
2512
|
-
if (subjects.length === 0) return null;
|
|
2513
|
-
const out = [];
|
|
2514
|
-
for (const subject of subjects) {
|
|
2515
|
-
if ((subject.x1 - subject.x0) * (subject.y1 - subject.y0) > TRACK_MAX_AREA) return null;
|
|
2516
|
-
const place = tracked(judged.aligned, judged.cut, subject);
|
|
2517
|
-
const stayed = Math.hypot(place.dx, place.dy) < STAYED && Math.abs(place.scale - 1) < 0.02;
|
|
2518
|
-
if (place.score < RELOCATED || stayed) return null;
|
|
2519
|
-
out.push({ subject, place });
|
|
2520
|
-
}
|
|
2521
|
-
return out;
|
|
2522
|
-
}
|
|
2523
|
-
function movedBox(box, place) {
|
|
2524
|
-
const cx = (box.x0 + box.x1) / 2;
|
|
2525
|
-
const cy = (box.y0 + box.y1) / 2;
|
|
2526
|
-
return {
|
|
2527
|
-
x0: cx + place.dx + place.scale * (box.x0 - cx),
|
|
2528
|
-
y0: cy + place.dy + place.scale * (box.y0 - cy),
|
|
2529
|
-
x1: cx + place.dx + place.scale * (box.x1 - cx),
|
|
2530
|
-
y1: cy + place.dy + place.scale * (box.y1 - cy)
|
|
2531
|
-
};
|
|
2532
|
-
}
|
|
2533
|
-
function cutMoved(cut, box, place) {
|
|
2534
|
-
const { width, height } = cut;
|
|
2535
|
-
const out = Buffer.alloc(width * height * 3);
|
|
2536
|
-
if (cut.channels === 3) cut.data.copy(out, 0, 0, width * height * 3);
|
|
2537
|
-
else for (let i = 0; i < width * height; i++) cut.data.copy(out, i * 3, i * cut.channels, i * cut.channels + 3);
|
|
2538
|
-
const to = movedBox(box, place);
|
|
2539
|
-
const margin = (ZONE_MARGIN + FEATHER) * Math.max((to.x1 - to.x0) * width, (to.y1 - to.y0) * height) + 4;
|
|
2540
|
-
const cx = (box.x0 + box.x1) / 2 * width;
|
|
2541
|
-
const cy = (box.y0 + box.y1) / 2 * height;
|
|
2542
|
-
for (let y = Math.max(0, Math.floor(to.y0 * height - margin)); y < Math.min(height, to.y1 * height + margin); y++) {
|
|
2543
|
-
for (let x = Math.max(0, Math.floor(to.x0 * width - margin)); x < Math.min(width, to.x1 * width + margin); x++) {
|
|
2544
|
-
const u = Math.min(width - 1.001, Math.max(0, cx + (x - cx - place.dx * width) / place.scale));
|
|
2545
|
-
const v = Math.min(height - 1.001, Math.max(0, cy + (y - cy - place.dy * height) / place.scale));
|
|
2546
|
-
const x0 = Math.floor(u);
|
|
2547
|
-
const y0 = Math.floor(v);
|
|
2548
|
-
const fx = u - x0;
|
|
2549
|
-
const fy = v - y0;
|
|
2550
|
-
for (let c = 0; c < 3; c++) {
|
|
2551
|
-
const at = (px, py) => cut.data[(py * width + px) * cut.channels + c] ?? 0;
|
|
2552
|
-
const top = at(x0, y0) * (1 - fx) + at(x0 + 1, y0) * fx;
|
|
2553
|
-
const bottom = at(x0, y0 + 1) * (1 - fx) + at(x0 + 1, y0 + 1) * fx;
|
|
2554
|
-
out[(y * width + x) * 3 + c] = Math.round(top * (1 - fy) + bottom * fy);
|
|
2555
|
-
}
|
|
2556
|
-
}
|
|
2557
|
-
}
|
|
2558
|
-
return { data: out, width, height, channels: 3 };
|
|
2559
|
-
}
|
|
2560
|
-
function putMoved(out, cut, r, j) {
|
|
2561
|
-
const full = { width: cut.width, height: cut.height };
|
|
2562
|
-
const moved = cutMoved(cut, r.subject, r.place);
|
|
2563
|
-
const judgedMoved = j.judged.cut.width === cut.width ? moved : shrinkRaw(moved, Math.round(cut.width / j.judged.cut.width));
|
|
2564
|
-
const diff = renderDiff(j.judged.aligned, judgedMoved, j.words);
|
|
2565
|
-
const mask = restoreMask(diff, [movedBox(r.subject, r.place)], j.words);
|
|
2566
|
-
return restoreSubjects(out, moved, enlarge(diff.gain, diff, full, 3), enlarge(mask, diff, full, 1));
|
|
2567
|
-
}
|
|
2568
|
-
|
|
2569
|
-
// src/engine/nodes/local/staticFormat.ts
|
|
2570
|
-
var Usage = z4.object({ model: z4.string(), inputTokens: z4.number(), outputTokens: z4.number(), costUsd: z4.number() }).strict();
|
|
2571
|
-
var Unit = z4.number().min(0).max(1);
|
|
2572
|
-
var SpanSchema = z4.object({ x0: Unit, y0: Unit, x1: Unit, y1: Unit }).strict();
|
|
2573
|
-
var Copy = z4.array(z4.object({ text: z4.string().min(1), role: z4.string().optional() }).strict());
|
|
2574
|
-
var Rect = z4.object({ x: z4.number(), y: z4.number(), width: z4.number(), height: z4.number() }).strict();
|
|
2575
|
-
var REASONS = [
|
|
2576
|
-
"band_too_far",
|
|
2577
|
-
"words_over_face",
|
|
2578
|
-
"subject_covered",
|
|
2579
|
-
"subject_cut",
|
|
2580
|
-
"words_misaligned",
|
|
2581
|
-
"words_not_read",
|
|
2582
|
-
"not_read"
|
|
2583
|
-
];
|
|
2584
|
-
var FormatFacts = z4.object({
|
|
2585
|
-
format: z4.string(),
|
|
2586
|
-
lead: z4.boolean(),
|
|
2587
|
-
plate: z4.object({ width: z4.number(), height: z4.number() }).strict(),
|
|
2588
|
-
crop: Rect,
|
|
2589
|
-
render_ratio: z4.string(),
|
|
2590
|
-
upscale: z4.number(),
|
|
2591
|
-
/** Each block of words code moved (into the band, off a face): its scale and shift, fractions of the size. */
|
|
2592
|
-
moves: z4.array(z4.object({ scale: z4.number(), dx: z4.number(), dy: z4.number() }).strict()),
|
|
2593
|
-
/** How far the words render sat from the cut once lined up (RMS grey levels): the spike's calibration of `MISALIGNED`. */
|
|
2594
|
-
residual: z4.number().optional(),
|
|
2595
|
-
reason: z4.enum(REASONS).optional(),
|
|
2596
|
-
/** `subject_covered`: the products and animals the words hide most of in this size, as the reader names them. */
|
|
2597
|
-
covered: z4.array(z4.string()).optional(),
|
|
2598
|
-
/**
|
|
2599
|
-
* How the words were laid: `whole`, the words model's whole render, its redrawn subjects put back
|
|
2600
|
-
* from the picture; `masked`, only what it changed at the words, on the picture (when its render
|
|
2601
|
-
* could not be taken whole twice). Absent when no words were set (`subject_cut`).
|
|
2602
|
-
*/
|
|
2603
|
-
words_mode: z4.enum(["whole", "masked"]).optional(),
|
|
2604
|
-
/** `whole`: the share of the picture's detail the render kept where it was (`keptShare`). */
|
|
2605
|
-
kept: z4.number().optional(),
|
|
2606
|
-
/** `whole`: the people, faces and products put back from the picture where the render redrew them. */
|
|
2607
|
-
restored: z4.array(z4.string()).optional(),
|
|
2608
|
-
/** Why the first render could not be taken whole, so the words were set a second time. */
|
|
2609
|
-
retried: z4.enum(WHOLE_PROBLEMS).optional(),
|
|
2610
|
-
/** `masked`: why the second render could not be taken whole either. */
|
|
2611
|
-
refused: z4.enum(WHOLE_PROBLEMS).optional(),
|
|
2612
|
-
reverted: z4.number(),
|
|
2613
|
-
/** `masked`: the mean difference from the cut outside the words layer, 0 by construction. */
|
|
2614
|
-
identity: z4.number().optional(),
|
|
2615
|
-
steps: z4.array(
|
|
2616
|
-
z4.object({
|
|
2617
|
-
name: z4.enum(["subjects", "words", "words_again", "boxes"]),
|
|
2618
|
-
cached: z4.boolean(),
|
|
2619
|
-
credits: z4.number(),
|
|
2620
|
-
ms: z4.number()
|
|
2621
|
-
}).strict()
|
|
2622
|
-
)
|
|
2623
|
-
}).strict();
|
|
2624
|
-
var Words = z4.object({
|
|
2625
|
-
model: z4.string().min(1),
|
|
2626
|
-
quality: z4.string().optional(),
|
|
2627
|
-
prompt: z4.string().min(1),
|
|
2628
|
-
/** A change to the words of this size (`--reword`, an autofix): the previous render (`inputs.previous`) leads. */
|
|
2629
|
-
edit: z4.object({ instruction: z4.string().min(1) }).strict().optional()
|
|
2630
|
-
}).strict();
|
|
2631
|
-
var MISALIGNED = 40;
|
|
2632
|
-
var FormatParamsSchema = z4.object({
|
|
2633
|
-
format: z4.string().regex(/^\d+(?:\.\d+)?:\d+(?:\.\d+)?$/),
|
|
2634
|
-
lead: z4.boolean().optional(),
|
|
2635
|
-
placements: z4.array(z4.string().min(1)).optional(),
|
|
2636
|
-
words: Words,
|
|
2637
|
-
copy: Copy.optional(),
|
|
2638
|
-
in_scene: z4.array(z4.string().min(1)).optional(),
|
|
2639
|
-
/** The plan's subject zone, fractions of the plate: what the cut centres on when no subject was read. */
|
|
2640
|
-
core: SpanSchema.optional(),
|
|
2641
|
-
band_min_scale: z4.number().min(0.5).max(1).optional(),
|
|
2642
|
-
/**
|
|
2643
|
-
* The other sizes cut from the same picture (a variant's lead is checked against them): when
|
|
2644
|
-
* one would cut a face, a product or a head, no words are paid for (`subject_cut`).
|
|
2645
|
-
*/
|
|
2646
|
-
check_formats: z4.array(z4.string().regex(/^\d+(?:\.\d+)?:\d+(?:\.\d+)?$/)).optional(),
|
|
2647
|
-
/**
|
|
2648
|
-
* The brand's logo file is placed on this size afterwards (`static_logo`): the words draw no logo
|
|
2649
|
-
* and keep its slot empty, planned here inside the size's safe area off every face and product,
|
|
2650
|
-
* from the logo's shape (`aspect`, width over height) and where the concept puts it (`position`).
|
|
2651
|
-
*/
|
|
2652
|
-
logo_slot: z4.object({ aspect: z4.number().positive(), position: z4.enum(LOGO_POSITIONS).optional() }).strict().optional()
|
|
2653
|
-
}).strict();
|
|
2654
|
-
var staticFormatNode = defineNode({
|
|
2655
|
-
id: "static_format",
|
|
2656
|
-
version: "1.9.0",
|
|
2657
|
-
category: "image",
|
|
2658
|
-
location: "local",
|
|
2659
|
-
billing: "reported",
|
|
2660
|
-
summary: "One delivered size of a static ad, from the ad's picture (`plate`): cuts the size at one scale around the people and products (`subjects`), and around the cards the picture draws for its words when it can, has the image model set the ad's designed words, logo and button on the cut (`params.words`, one paid call), and takes its whole render, lined up with the cut, putting back from the picture each person, face and product it redrew (soft-edged, in the render's light). Words the frame cuts, the app covers or a face sits under are moved by code on the render, with the band, card or pill they sit on. A render that moved the picture or redrew a subject, or whose words no move clears, is set again once; when that one moved or redrew too, only what it changed at the words is kept on the picture, and when its words are out of place twice, it stays whole and says where they are. Outputs the size (`image`), the cut it was set on (`cut`), the model's render (`words_raw`), where the words were laid when only they were kept (`words_alpha`) and what it did (`facts`).",
|
|
2661
|
-
when_to_use: "One per delivered size of a static ad built on one picture (`format_<size>`, role `static_format`). One image call at the words model's price (two when the first cannot be taken whole), kept apart from the free cut and compose: changing only the band, the placements or the node's version never pays for it again; `--regenerate format_<size>` does.",
|
|
2662
|
-
inputs: z4.object({
|
|
2663
|
-
plate: ImageRef,
|
|
2664
|
-
subjects: z4.array(SubjectBoxSchema).optional(),
|
|
2665
|
-
logo: ImageRef.optional(),
|
|
2666
|
-
type: ImageRef.optional(),
|
|
2667
|
-
style: ImageRef.optional(),
|
|
2668
|
-
refs: z4.array(ImageRef).optional(),
|
|
2669
|
-
previous: ImageRef.optional()
|
|
2670
|
-
}).strict(),
|
|
2671
|
-
params: FormatParamsSchema,
|
|
2672
|
-
outputs: z4.object({
|
|
2673
|
-
image: ImageRef,
|
|
2674
|
-
cut: ImageRef,
|
|
2675
|
-
words_raw: ImageRef.optional(),
|
|
2676
|
-
words_alpha: ImageRef.optional(),
|
|
2677
|
-
/** The slot the words left empty for the logo file, in the size's pixels (`params.logo_slot`). */
|
|
2678
|
-
logo_slot: Rect.optional(),
|
|
2679
|
-
facts: FormatFacts,
|
|
2680
|
-
usage: z4.array(Usage).optional()
|
|
2681
|
-
}).strict(),
|
|
2682
|
-
outputKinds: { image: "image", cut: "image", words_raw: "image", words_alpha: "image" },
|
|
2683
|
-
cost: ({ params }) => imageGenerateNode.cost?.({ params: wordsCallParams(params.words, "1:1") }) ?? {
|
|
2684
|
-
credits: 0,
|
|
2685
|
-
seconds_estimate: 60
|
|
2686
|
-
},
|
|
2687
|
-
async execute({ inputs, params, ctx }) {
|
|
2688
|
-
return await formatSize(inputs, params, ctx);
|
|
2689
|
-
}
|
|
2690
|
-
});
|
|
2691
|
-
function wordsCallParams(words, ratio) {
|
|
2692
|
-
const prompt = words.edit ? `Edit: ${words.edit.instruction}
|
|
2693
|
-
|
|
2694
|
-
${words.prompt}` : words.prompt;
|
|
2695
|
-
return { model: words.model, prompt, aspect_ratio: ratio, ...words.quality ? { quality: words.quality } : {} };
|
|
2696
|
-
}
|
|
2697
|
-
var percent = (v) => Math.round(Math.max(0, Math.min(1, v)) * 100);
|
|
2698
|
-
function wordsAvoid(faces) {
|
|
2699
|
-
if (faces.length === 0) return "";
|
|
2700
|
-
const where = faces.map(
|
|
2701
|
-
(f) => `a face ${percent(f.y0)}-${percent(f.y1)}% down, ${percent(f.x0)}-${percent(f.x1)}% across`
|
|
2702
|
-
);
|
|
2703
|
-
return `Keep every word, the logo and the button off ${where.join("; ")}.`;
|
|
2704
|
-
}
|
|
2705
|
-
function stripsOf(before, after, sides, depth) {
|
|
2706
|
-
const cut = [
|
|
2707
|
-
...percent(before) >= 1 ? [{ share: percent(before), side: sides[0] }] : [],
|
|
2708
|
-
...percent(after) >= 1 ? [{ share: percent(after), side: sides[1] }] : []
|
|
2709
|
-
];
|
|
2710
|
-
return cut.map(
|
|
2711
|
-
(s, i) => i === 0 ? `a strip ${s.share}% ${depth} at its ${s.side}` : `${s.share}% at its ${s.side}`
|
|
2712
|
-
);
|
|
2713
|
-
}
|
|
2714
|
-
function wordsFrame(frame, work, format) {
|
|
2715
|
-
const down = stripsOf(frame.y / work.height, 1 - (frame.y + frame.height) / work.height, ["top", "bottom"], "deep");
|
|
2716
|
-
const across2 = stripsOf(frame.x / work.width, 1 - (frame.x + frame.width) / work.width, ["left", "right"], "wide");
|
|
2717
|
-
const strips = [down.join(" and "), across2.join(" and ")].filter(Boolean);
|
|
2718
|
-
if (strips.length === 0) return "";
|
|
2719
|
-
const many = down.length + across2.length > 1;
|
|
2720
|
-
return `This picture is cut to ${format} afterwards: ${strips.join(", and ")} ${many ? "are" : "is"} cut away, so no word, logo or button comes near ${many ? "them" : "it"}.`;
|
|
2721
|
-
}
|
|
2722
|
-
function splitWordBoxes(boxes, copy, inScene) {
|
|
2723
|
-
const logoWords = copy.filter((l) => /logo/i.test(l.role ?? "")).map((l) => l.text);
|
|
2724
|
-
const read = withWordmark(boxes, logoWords);
|
|
2725
|
-
const lines = copy.filter((l) => !/logo/i.test(l.role ?? ""));
|
|
2726
|
-
const ours = new Set(lines.flatMap((line) => boxesOfLine(line, read)));
|
|
2727
|
-
const unnamed = (b) => lines.length > 0 && b.kind === "text" && !ours.has(b);
|
|
2728
|
-
const scene = /* @__PURE__ */ new Set([...inScene.flatMap((text) => boxesOfLine({ text }, read)), ...read.filter(unnamed)]);
|
|
2729
|
-
const buttons = new Set(copy.filter(isButtonLine).flatMap((line) => boxesOfLine(line, read)));
|
|
2730
|
-
return {
|
|
2731
|
-
designed: read.filter((b) => !scene.has(b)).map((b) => buttons.has(b) ? buttonShape(b) : b),
|
|
2732
|
-
inScene: read.filter((b) => scene.has(b))
|
|
2733
|
-
};
|
|
2734
|
-
}
|
|
2735
|
-
function toFrame(b, frame, work) {
|
|
2736
|
-
return {
|
|
2737
|
-
...b,
|
|
2738
|
-
x0: (b.x0 * work.width - frame.x) / frame.width,
|
|
2739
|
-
y0: (b.y0 * work.height - frame.y) / frame.height,
|
|
2740
|
-
x1: (b.x1 * work.width - frame.x) / frame.width,
|
|
2741
|
-
y1: (b.y1 * work.height - frame.y) / frame.height
|
|
2742
|
-
};
|
|
2743
|
-
}
|
|
2744
|
-
function subjectIn(b, rect, plate) {
|
|
2745
|
-
return {
|
|
2746
|
-
...b,
|
|
2747
|
-
x0: (b.x0 * plate.width - rect.x) / rect.width,
|
|
2748
|
-
y0: (b.y0 * plate.height - rect.y) / rect.height,
|
|
2749
|
-
x1: (b.x1 * plate.width - rect.x) / rect.width,
|
|
2750
|
-
y1: (b.y1 * plate.height - rect.y) / rect.height
|
|
2751
|
-
};
|
|
2752
|
-
}
|
|
2753
|
-
var visible = (b) => b.x1 > 0 && b.y1 > 0 && b.x0 < 1 && b.y0 < 1;
|
|
2754
|
-
async function frameSize(plateBytes, crop, format) {
|
|
2755
|
-
const delivered = deliveredSize(format);
|
|
2756
|
-
const upscale = delivered.width / crop.rect.width;
|
|
2757
|
-
const work = { width: Math.round(crop.render.width * upscale), height: Math.round(crop.render.height * upscale) };
|
|
2758
|
-
const frame = {
|
|
2759
|
-
x: Math.min(work.width - delivered.width, Math.max(0, Math.round((crop.rect.x - crop.render.x) * upscale))),
|
|
2760
|
-
y: Math.min(work.height - delivered.height, Math.max(0, Math.round((crop.rect.y - crop.render.y) * upscale))),
|
|
2761
|
-
width: delivered.width,
|
|
2762
|
-
height: delivered.height
|
|
2763
|
-
};
|
|
2764
|
-
const region = { left: crop.render.x, top: crop.render.y, width: crop.render.width, height: crop.render.height };
|
|
2765
|
-
const renderCut = await rawOf(await sharp4(plateBytes).extract(region).toBuffer(), work);
|
|
2766
|
-
return { upscale, work, frame, renderCut, cutFrame: cropRaw(renderCut, frame) };
|
|
2767
|
-
}
|
|
2768
|
-
function slotOnWork(rect, frame, work) {
|
|
2769
|
-
return {
|
|
2770
|
-
x0: (frame.x + rect.x) / work.width,
|
|
2771
|
-
y0: (frame.y + rect.y) / work.height,
|
|
2772
|
-
x1: (frame.x + rect.x + rect.width) / work.width,
|
|
2773
|
-
y1: (frame.y + rect.y + rect.height) / work.height
|
|
2774
|
-
};
|
|
2775
|
-
}
|
|
2776
|
-
var LOGO_AVOIDS = /* @__PURE__ */ new Set(["person", "face", "product", "animal"]);
|
|
2777
|
-
var PLAIN_GROUND_SPREAD = 18;
|
|
2778
|
-
function sizeLogoSlot(params, frame, work, subjects, picture) {
|
|
2779
|
-
if (!params.logo_slot) return void 0;
|
|
2780
|
-
const avoid = subjects.filter((s) => LOGO_AVOIDS.has(s.kind)).map((s) => ({
|
|
2781
|
-
x: Math.round(s.x0 * work.width - frame.x),
|
|
2782
|
-
y: Math.round(s.y0 * work.height - frame.y),
|
|
2783
|
-
width: Math.round((s.x1 - s.x0) * work.width),
|
|
2784
|
-
height: Math.round((s.y1 - s.y0) * work.height)
|
|
2785
|
-
}));
|
|
2786
|
-
const size = { width: frame.width, height: frame.height };
|
|
2787
|
-
const safe = blockingArea(params.format, params.placements);
|
|
2788
|
-
const shape = {
|
|
2789
|
-
aspect: params.logo_slot.aspect,
|
|
2790
|
-
...params.logo_slot.position ? { position: params.logo_slot.position } : {}
|
|
2791
|
-
};
|
|
2792
|
-
const spot = chooseLogoSpot(size, safe, {
|
|
2793
|
-
...shape,
|
|
2794
|
-
subjects: avoid,
|
|
2795
|
-
...picture ? {
|
|
2796
|
-
calm: (r) => spreadUnder(picture, r) <= PLAIN_GROUND_SPREAD,
|
|
2797
|
-
crossesEdge: (r) => straightEdgeIn(picture, r)
|
|
2798
|
-
} : {}
|
|
2799
|
-
});
|
|
2800
|
-
return spot?.slot ?? designerSpots(size, safe, shape)[0];
|
|
2801
|
-
}
|
|
2802
|
-
async function setWords(inputs, params, run, shown, again) {
|
|
2803
|
-
const callParams = wordsCallParams(params.words, shown.ratio);
|
|
2804
|
-
const prompt = [
|
|
2805
|
-
callParams.prompt,
|
|
2806
|
-
wordsAvoid(shown.faces),
|
|
2807
|
-
wordsFrame(shown.frame, shown.work, params.format),
|
|
2808
|
-
shown.logo ? logoSlotLine(slotOnWork(shown.logo.clear, shown.frame, shown.work), shown.logo.position) : "",
|
|
2809
|
-
again
|
|
2810
|
-
].filter(Boolean).join("\n\n");
|
|
2811
|
-
const references = [
|
|
2812
|
-
...inputs.previous && params.words.edit ? [inputs.previous] : [],
|
|
2813
|
-
shown.cut,
|
|
2814
|
-
...[inputs.logo, inputs.type, inputs.style].filter((r) => r !== void 0),
|
|
2815
|
-
...inputs.refs ?? []
|
|
2816
|
-
];
|
|
2817
|
-
const call = { ...callParams, prompt };
|
|
2818
|
-
return await timedStep(
|
|
2819
|
-
run,
|
|
2820
|
-
again ? "words_again" : "words",
|
|
2821
|
-
{ params: call, references: references.map((r) => r.sha256) },
|
|
2822
|
-
async (suffix, stepCtx) => {
|
|
2823
|
-
const out = await callBackendExec({
|
|
2824
|
-
nodeType: imageGenerateNode.id,
|
|
2825
|
-
nodeVersion: imageGenerateNode.version,
|
|
2826
|
-
params: imageGenerateNode.params.parse(call),
|
|
2827
|
-
inputs: { reference: references },
|
|
2828
|
-
outputKinds: { images: "image" },
|
|
2829
|
-
ctx: stepCtx,
|
|
2830
|
-
idempotencySuffix: suffix
|
|
2831
|
-
});
|
|
2832
|
-
const first = Array.isArray(out.images) ? out.images[0] : void 0;
|
|
2833
|
-
if (!first) throw new Error("static_format: the words call returned no picture");
|
|
2834
|
-
return first;
|
|
2835
|
-
}
|
|
2836
|
-
);
|
|
2837
|
-
}
|
|
2838
|
-
function wordsRead(reading, params) {
|
|
2839
|
-
if (reading.status === "read") return splitWordBoxes(reading.boxes, params.copy ?? [], params.in_scene ?? []);
|
|
2840
|
-
return { designed: [{ text: "", kind: "text", x0: 0, y0: 0, x1: 1, y1: 1 }], inScene: [] };
|
|
2841
|
-
}
|
|
2842
|
-
function liftWords(render, framing, words, subjects, params, keepOut = []) {
|
|
2843
|
-
const faces = subjects.filter((b) => b.kind === "face");
|
|
2844
|
-
const fit = registerToSource(render, framing.renderCut, words.read ? words.designed : []);
|
|
2845
|
-
const misaligned = fit.residual > MISALIGNED;
|
|
2846
|
-
const aligned = misaligned ? render : warpToCut(render, fit);
|
|
2847
|
-
const { layer, reverted } = wordsLayer(aligned, framing.renderCut, words.designed, {
|
|
2848
|
-
subjects,
|
|
2849
|
-
keepOut: words.inScene,
|
|
2850
|
-
boxesOnly: misaligned
|
|
2851
|
-
});
|
|
2852
|
-
const sized = cropLayer(layer, framing.frame);
|
|
2853
|
-
const inFrame = (b) => toFrame(b, framing.frame, framing.work);
|
|
2854
|
-
const frameFaces = faces.map(inFrame).filter(visible);
|
|
2855
|
-
const frameWords = words.designed.map(inFrame).filter(visible);
|
|
2856
|
-
const plan = words.read ? planWordsShift(frameWords, params.format, {
|
|
2857
|
-
...params.placements ? { placements: params.placements } : {},
|
|
2858
|
-
...params.band_min_scale !== void 0 ? { minScale: params.band_min_scale } : {},
|
|
2859
|
-
// A move never takes words into the slot kept for the logo, nor is made for it: moving a block
|
|
2860
|
-
// out of it cut its band through the letters (Cubicup 4:5, round 6); the logo goes elsewhere.
|
|
2861
|
-
faces: frameFaces,
|
|
2862
|
-
keepOut
|
|
2863
|
-
}) : { moves: [] };
|
|
2864
|
-
const { layer: placed2, covered } = protectFaces(
|
|
2865
|
-
plan.moves.length > 0 ? moveLayer(sized, plan.moves) : sized,
|
|
2866
|
-
frameFaces
|
|
2867
|
-
);
|
|
2868
|
-
const hidden = coveredSubjects(placed2, subjects.map(inFrame).filter(visible));
|
|
2869
|
-
const reason = (words.read ? void 0 : "words_not_read") ?? plan.reason ?? (misaligned ? "words_misaligned" : void 0) ?? (covered > 0 ? "words_over_face" : void 0) ?? (hidden.length > 0 ? "subject_covered" : void 0);
|
|
2870
|
-
return {
|
|
2871
|
-
out: compose(framing.cutFrame, placed2),
|
|
2872
|
-
alpha: placed2.alpha,
|
|
2873
|
-
moves: plan.moves,
|
|
2874
|
-
residual: fit.residual,
|
|
2875
|
-
reverted,
|
|
2876
|
-
covered: hidden,
|
|
2877
|
-
...reason ? { reason } : {}
|
|
2878
|
-
};
|
|
2879
|
-
}
|
|
2880
|
-
async function alphaPng(alpha, width, height) {
|
|
2881
|
-
return await sharp4(Buffer.from(alpha.map((a) => Math.round(a * 255))), { raw: { width, height, channels: 1 } }).png().toBuffer();
|
|
2882
|
-
}
|
|
2883
|
-
var round3 = (v) => Math.round(v * 1e3) / 1e3;
|
|
2884
|
-
var round1 = (v) => Math.round(v * 10) / 10;
|
|
2885
|
-
function liftedFacts(base, lifted, run) {
|
|
2886
|
-
const reason = lifted.reason ?? (run.subjectsRead ? void 0 : "not_read");
|
|
2887
|
-
return {
|
|
2888
|
-
...base,
|
|
2889
|
-
...reason ? { reason } : {},
|
|
2890
|
-
...lifted.covered.length > 0 ? { covered: lifted.covered } : {},
|
|
2891
|
-
moves: lifted.moves.map(({ fit }) => ({ scale: round3(fit.scale), dx: round3(fit.dx), dy: round3(fit.dy) })),
|
|
2892
|
-
residual: round1(lifted.residual),
|
|
2893
|
-
reverted: round3(lifted.reverted),
|
|
2894
|
-
identity: run.identity,
|
|
2895
|
-
steps: run.steps
|
|
2896
|
-
};
|
|
2897
|
-
}
|
|
2898
|
-
async function wordsTry(inputs, params, run, shown, again) {
|
|
2899
|
-
const ref = await setWords(inputs, params, run, shown, again);
|
|
2900
|
-
const { bytes } = await imageOf(ref, "static_format");
|
|
2901
|
-
const reading = await timedStep(
|
|
2902
|
-
run,
|
|
2903
|
-
"boxes",
|
|
2904
|
-
{ render: ref.sha256 },
|
|
2905
|
-
() => readTextBoxes(bytes, { onSpend: (u) => run.usage.push(u) }),
|
|
2906
|
-
{ keep: isRead }
|
|
2907
|
-
);
|
|
2908
|
-
return { ref, render: await rawOf(bytes, run.framing.work), reading };
|
|
2909
|
-
}
|
|
2910
|
-
function takeOf(t, run, subjects, params) {
|
|
2911
|
-
const { framing } = run;
|
|
2912
|
-
return takeWhole({
|
|
2913
|
-
render: t.render,
|
|
2914
|
-
cut: framing.renderCut,
|
|
2915
|
-
frame: framing.frame,
|
|
2916
|
-
words: t.reading.status === "read" ? splitWordBoxes(t.reading.boxes, params.copy ?? [], params.in_scene ?? []) : null,
|
|
2917
|
-
subjects,
|
|
2918
|
-
format: params.format,
|
|
2919
|
-
...params.placements ? { placements: params.placements } : {},
|
|
2920
|
-
...params.band_min_scale !== void 0 ? { minScale: params.band_min_scale } : {},
|
|
2921
|
-
...run.keepOut.length > 0 ? { keepOut: run.keepOut } : {}
|
|
2922
|
-
});
|
|
2923
|
-
}
|
|
2924
|
-
var quoted = (what) => what.map((w) => `"${w}"`).join(", ");
|
|
2925
|
-
function setAgain(problem, what) {
|
|
2926
|
-
const said = {
|
|
2927
|
-
picture_moved: "Your last version moved or resized the picture to make room for the words. Keep Image 1 exactly where it is, at its size and framing; lay any band or card over its edge instead.",
|
|
2928
|
-
subject_redrawn: `Your last version drew ${what.join(" and ") || "a person"} again, elsewhere or in another pose. Keep them exactly as Image 1 shows them, where they are, and set the words around them.`,
|
|
2929
|
-
words_lost: `Your last version set ${quoted(what) || "words"} where this size is cut, the app's buttons cover them or the logo goes. Keep every word and the button inside the zones and out of the place kept plain for the logo.`,
|
|
2930
|
-
words_over_face: "Your last version set words over a face. Keep every word, the logo and the button off every face."
|
|
2931
|
-
};
|
|
2932
|
-
return said[problem];
|
|
2933
|
-
}
|
|
2934
|
-
var MISPLACED = {
|
|
2935
|
-
words_lost: "band_too_far",
|
|
2936
|
-
words_over_face: "words_over_face"
|
|
2937
|
-
};
|
|
2938
|
-
async function wholeSize(take2, t, run, base, why = {}) {
|
|
2939
|
-
const { retried } = why;
|
|
2940
|
-
const image = await run.ctx.assets.ingestBytes({
|
|
2941
|
-
bytes: await pngOf(take2.out),
|
|
2942
|
-
kind: "image",
|
|
2943
|
-
mime: "image/png"
|
|
2944
|
-
});
|
|
2945
|
-
const reason = (t.reading.status === "read" ? void 0 : "words_not_read") ?? (why.misplaced ? MISPLACED[why.misplaced] : void 0) ?? (take2.covered.length > 0 ? "subject_covered" : void 0) ?? (run.subjectsRead ? void 0 : "not_read");
|
|
2946
|
-
const facts = {
|
|
2947
|
-
...base,
|
|
2948
|
-
...reason ? { reason } : {},
|
|
2949
|
-
...take2.covered.length > 0 ? { covered: take2.covered } : {},
|
|
2950
|
-
words_mode: "whole",
|
|
2951
|
-
kept: round3(take2.kept),
|
|
2952
|
-
...take2.restored.length > 0 ? { restored: take2.restored } : {},
|
|
2953
|
-
...retried ? { retried } : {},
|
|
2954
|
-
moves: take2.moves.map(({ fit }) => ({ scale: round3(fit.scale), dx: round3(fit.dx), dy: round3(fit.dy) })),
|
|
2955
|
-
residual: round1(take2.residual),
|
|
2956
|
-
reverted: 0,
|
|
2957
|
-
steps: run.steps
|
|
2958
|
-
};
|
|
2959
|
-
run.ctx.log(
|
|
2960
|
-
`${base.format}: cut ${base.crop.width}x${base.crop.height} (x${base.upscale}), words whole${take2.moves.length > 0 ? ", moved" : ""}${take2.restored.length > 0 ? `, ${take2.restored.join(", ")} put back` : ""}${retried ? ` (set again: ${retried})` : ""}${reason ? `, ${reason}` : ""}`
|
|
2961
|
-
);
|
|
2962
|
-
return { image, cut: run.cut, words_raw: t.ref, facts };
|
|
2963
|
-
}
|
|
2964
|
-
async function maskedSize(t, run, base, subjects, params, why) {
|
|
2965
|
-
const { framing } = run;
|
|
2966
|
-
const lifted = liftWords(
|
|
2967
|
-
t.render,
|
|
2968
|
-
framing,
|
|
2969
|
-
{ ...wordsRead(t.reading, params), read: t.reading.status === "read" },
|
|
2970
|
-
subjects,
|
|
2971
|
-
params,
|
|
2972
|
-
run.keepOut
|
|
2973
|
-
);
|
|
2974
|
-
const identity = identityOutside(lifted.out, framing.cutFrame, lifted.alpha);
|
|
2975
|
-
if (identity !== 0) throw new Error(`static_format: the picture outside the words changed (${identity})`);
|
|
2976
|
-
const [image, alpha] = await Promise.all([
|
|
2977
|
-
run.ctx.assets.ingestBytes({ bytes: await pngOf(lifted.out), kind: "image", mime: "image/png" }),
|
|
2978
|
-
run.ctx.assets.ingestBytes({
|
|
2979
|
-
bytes: await alphaPng(lifted.alpha, framing.frame.width, framing.frame.height),
|
|
2980
|
-
kind: "image",
|
|
2981
|
-
mime: "image/png"
|
|
2982
|
-
})
|
|
2983
|
-
]);
|
|
2984
|
-
const facts = {
|
|
2985
|
-
...liftedFacts(base, lifted, { identity, steps: run.steps, subjectsRead: run.subjectsRead }),
|
|
2986
|
-
words_mode: "masked",
|
|
2987
|
-
retried: why.retried,
|
|
2988
|
-
refused: why.refused
|
|
2989
|
-
};
|
|
2990
|
-
run.ctx.log(
|
|
2991
|
-
`${base.format}: cut ${base.crop.width}x${base.crop.height} (x${base.upscale}), words set twice (${why.retried}, then ${why.refused}), only the words kept${lifted.moves.length > 0 ? ", moved" : ""}${facts.reason ? `, ${facts.reason}` : ""}`
|
|
2992
|
-
);
|
|
2993
|
-
return { image, cut: run.cut, words_raw: t.ref, words_alpha: alpha, facts };
|
|
2994
|
-
}
|
|
2995
|
-
async function formatSize(inputs, params, ctx) {
|
|
2996
|
-
const { bytes: plateBytes, size: plate } = await imageOf(inputs.plate, "static_format");
|
|
2997
|
-
const steps = [];
|
|
2998
|
-
const usage = [];
|
|
2999
|
-
const spend = () => usage.length > 0 ? { usage } : {};
|
|
3000
|
-
const subjects = inputs.subjects ?? await subjectsStep({ ctx, steps, usage }, inputs.plate, plateBytes);
|
|
3001
|
-
const ratios = supportedAspectRatios("image_generate", params.words.model);
|
|
3002
|
-
const panels = platePanels(await rawOf(plateBytes, plate));
|
|
3003
|
-
const crop = subjectCrop(plate, params.format, subjects, {
|
|
3004
|
-
...params.core ? { core: params.core } : {},
|
|
3005
|
-
...ratios ? { renderRatios: ratios } : {},
|
|
3006
|
-
...panels.length > 0 ? { panels } : {}
|
|
3007
|
-
});
|
|
3008
|
-
const framing = await frameSize(plateBytes, crop, params.format);
|
|
3009
|
-
const cutRef = await ctx.assets.ingestBytes({
|
|
3010
|
-
bytes: await pngOf(framing.cutFrame),
|
|
3011
|
-
kind: "image",
|
|
3012
|
-
mime: "image/png"
|
|
3013
|
-
});
|
|
3014
|
-
const base = {
|
|
3015
|
-
format: params.format,
|
|
3016
|
-
lead: params.lead ?? false,
|
|
3017
|
-
plate,
|
|
3018
|
-
crop: crop.rect,
|
|
3019
|
-
render_ratio: crop.render.ratio,
|
|
3020
|
-
upscale: round3(framing.upscale)
|
|
3021
|
-
};
|
|
3022
|
-
const shownSubjects = (subjects ?? []).map((s) => subjectIn(s, crop.render, plate)).filter(visible);
|
|
3023
|
-
const logo = sizeLogoSlot(params, framing.frame, framing.work, shownSubjects, greyTable(framing.cutFrame));
|
|
3024
|
-
const slotOut = logo ? { logo_slot: logo.rect } : {};
|
|
3025
|
-
const elsewhere = (params.check_formats ?? []).filter((f) => f !== params.format).map((f) => ({ format: f, crop: subjectCrop(plate, f, subjects, params.core ? { core: params.core } : {}) })).find((c) => !c.crop.fits);
|
|
3026
|
-
if (!crop.fits || elsewhere) {
|
|
3027
|
-
const lost = crop.fits && elsewhere ? `${elsewhere.format}: ${elsewhere.crop.cut.join(", ")}` : crop.cut.join(", ");
|
|
3028
|
-
ctx.log(`${params.format}: no cut keeps ${lost} whole; the words were not set`);
|
|
3029
|
-
const facts = { ...base, reason: "subject_cut", moves: [], reverted: 0, identity: 0, steps };
|
|
3030
|
-
return { image: cutRef, cut: cutRef, facts, ...slotOut, ...spend() };
|
|
3031
|
-
}
|
|
3032
|
-
const shownCut = await ctx.assets.ingestBytes({
|
|
3033
|
-
bytes: await pngOf(framing.renderCut),
|
|
3034
|
-
kind: "image",
|
|
3035
|
-
mime: "image/png"
|
|
3036
|
-
});
|
|
3037
|
-
const keepOut = logo ? [
|
|
3038
|
-
{
|
|
3039
|
-
x0: logo.clear.x / framing.frame.width,
|
|
3040
|
-
y0: logo.clear.y / framing.frame.height,
|
|
3041
|
-
x1: (logo.clear.x + logo.clear.width) / framing.frame.width,
|
|
3042
|
-
y1: (logo.clear.y + logo.clear.height) / framing.frame.height
|
|
3043
|
-
}
|
|
3044
|
-
] : [];
|
|
3045
|
-
const run = { ctx, steps, usage, subjectsRead: subjects !== null, cut: cutRef, framing, keepOut };
|
|
3046
|
-
const shown = {
|
|
3047
|
-
cut: shownCut,
|
|
3048
|
-
ratio: crop.render.ratio,
|
|
3049
|
-
faces: shownSubjects.filter((s) => s.kind === "face"),
|
|
3050
|
-
frame: framing.frame,
|
|
3051
|
-
work: framing.work,
|
|
3052
|
-
...logo ? { logo } : {}
|
|
3053
|
-
};
|
|
3054
|
-
return { ...await letter({ inputs, params, run, shown, base, subjects: shownSubjects }), ...slotOut, ...spend() };
|
|
3055
|
-
}
|
|
3056
|
-
async function letter(l) {
|
|
3057
|
-
const { inputs, params, run, shown, base, subjects } = l;
|
|
3058
|
-
const first = await wordsTry(inputs, params, run, shown);
|
|
3059
|
-
const firstTake = takeOf(first, run, subjects, params);
|
|
3060
|
-
if (firstTake.ok) return await wholeSize(firstTake, first, run, base);
|
|
3061
|
-
const second = await wordsTry(inputs, params, run, shown, setAgain(firstTake.problem, firstTake.what));
|
|
3062
|
-
const secondTake = takeOf(second, run, subjects, params);
|
|
3063
|
-
const retried = firstTake.problem;
|
|
3064
|
-
if (secondTake.ok) return await wholeSize(secondTake, second, run, base, { retried });
|
|
3065
|
-
const placed2 = [
|
|
3066
|
-
{ take: secondTake, t: second },
|
|
3067
|
-
{ take: firstTake, t: first }
|
|
3068
|
-
].find((p) => p.take.whole);
|
|
3069
|
-
const whole = placed2?.take.whole;
|
|
3070
|
-
if (placed2 && whole) {
|
|
3071
|
-
const taken = { ...whole, moves: [], kept: placed2.take.kept, residual: placed2.take.residual };
|
|
3072
|
-
return await wholeSize(taken, placed2.t, run, base, { retried, misplaced: placed2.take.problem });
|
|
3073
|
-
}
|
|
3074
|
-
const kept = secondTake.kept >= firstTake.kept ? second : first;
|
|
3075
|
-
return await maskedSize(kept, run, base, subjects, params, { retried, refused: secondTake.problem });
|
|
3076
|
-
}
|
|
3077
|
-
|
|
3078
|
-
// src/engine/nodes/local/staticLogo.ts
|
|
3079
|
-
import sharp5 from "sharp";
|
|
3080
|
-
import { z as z5 } from "zod";
|
|
3081
|
-
var Usage2 = z5.object({ model: z5.string(), inputTokens: z5.number(), outputTokens: z5.number(), costUsd: z5.number() }).strict();
|
|
3082
|
-
var Rect2 = z5.object({
|
|
3083
|
-
x: z5.number().int(),
|
|
3084
|
-
y: z5.number().int(),
|
|
3085
|
-
width: z5.number().int().positive(),
|
|
3086
|
-
height: z5.number().int().positive()
|
|
3087
|
-
}).strict();
|
|
3088
|
-
var Copy2 = z5.array(z5.object({ text: z5.string().min(1), role: z5.string().optional() }).strict());
|
|
3089
|
-
var LogoFacts = z5.object({
|
|
3090
|
-
format: z5.string(),
|
|
3091
|
-
/** Where the logo file was placed, in the size's pixels. */
|
|
3092
|
-
box: Rect2,
|
|
3093
|
-
/** The slot it was placed in. */
|
|
3094
|
-
slot: Rect2,
|
|
3095
|
-
/** Which of the logo's files (`inputs.logos`) was placed: the one that reads on the ground. */
|
|
3096
|
-
variant: z5.number().int().nonnegative(),
|
|
3097
|
-
/** The colour a file inked in the text colour (`currentColor`) was drawn in, of `params.inks`. */
|
|
3098
|
-
ink: z5.string().optional(),
|
|
3099
|
-
/** The grey level (0-255) of the ground under the logo, and the contrast of the placed file's ink on it. */
|
|
3100
|
-
ground: z5.number(),
|
|
3101
|
-
contrast: z5.number(),
|
|
3102
|
-
/** The share (0-1) of the logo's solid pixels whose own ground it stands out from; the review blocks under 0.9. */
|
|
3103
|
-
legible: z5.number().default(1),
|
|
3104
|
-
/** Set when no plain ground held the logo and it sits on a plate of its own: the plate's colour. */
|
|
3105
|
-
plate: z5.object({ colour: z5.string() }).strict().optional(),
|
|
3106
|
-
/** The headline's box as read on the size; null when the ad has no copy or it was not read. */
|
|
3107
|
-
headline: Rect2.nullable(),
|
|
3108
|
-
/** Whether the size's words were read for the check. */
|
|
3109
|
-
read: z5.boolean(),
|
|
3110
|
-
problems: z5.array(
|
|
3111
|
-
z5.object({
|
|
3112
|
-
element: z5.enum(PLACEMENT_ELEMENTS),
|
|
3113
|
-
defect: z5.enum(PLACEMENT_DEFECTS),
|
|
3114
|
-
edges: z5.array(z5.enum(PLACEMENT_EDGES))
|
|
3115
|
-
}).strict()
|
|
3116
|
-
)
|
|
3117
|
-
}).strict();
|
|
3118
|
-
var LogoParams = z5.object({
|
|
3119
|
-
format: z5.string().regex(/^\d+(?:\.\d+)?:\d+(?:\.\d+)?$/),
|
|
3120
|
-
placements: z5.array(z5.string().min(1)).optional(),
|
|
3121
|
-
/** Where the concept places the logo (`logo_position`, read by `placedLogoPosition`). */
|
|
3122
|
-
position: z5.enum(LOGO_POSITIONS).optional(),
|
|
3123
|
-
/** The logo's shape the slot was planned with (width over height), as the size's brief reserved it; else the file's own. */
|
|
3124
|
-
aspect: z5.number().positive().optional(),
|
|
3125
|
-
/** The ad's copy (prompt.json's lines and their roles): the check finds the headline among them. */
|
|
3126
|
-
copy: Copy2.optional(),
|
|
3127
|
-
/** The colours a logo inked in the text colour (`currentColor`) may be drawn in: the brand's, and white. */
|
|
3128
|
-
inks: z5.array(z5.string().regex(/^#[0-9a-fA-F]{6}$/)).min(1).optional(),
|
|
3129
|
-
/** The brand's colours a plate behind the logo may take, when no plain ground holds it. */
|
|
3130
|
-
plates: z5.array(z5.string().regex(/^#[0-9a-fA-F]{6}$/)).optional()
|
|
3131
|
-
}).strict();
|
|
3132
|
-
async function logoFileOf(logo) {
|
|
3133
|
-
return await textColourSource(logo.metadata) ?? (await imageOf(logo, "static_logo")).bytes;
|
|
3134
|
-
}
|
|
3135
|
-
var DEFAULT_INKS = ["#000000", "#FFFFFF"];
|
|
3136
|
-
async function candidatesOf(files, inks) {
|
|
3137
|
-
const each = await Promise.all(
|
|
3138
|
-
files.map(async (bytes, file) => {
|
|
3139
|
-
if (!usesTextColour(bytes)) return [{ file, art: await logoArtwork(bytes) }];
|
|
3140
|
-
return await Promise.all(
|
|
3141
|
-
(inks ?? DEFAULT_INKS).map(async (ink) => ({
|
|
3142
|
-
file,
|
|
3143
|
-
ink,
|
|
3144
|
-
art: await logoArtwork(bytes, { currentColor: ink })
|
|
3145
|
-
}))
|
|
3146
|
-
);
|
|
3147
|
-
})
|
|
3148
|
-
);
|
|
3149
|
-
return each.flat();
|
|
3150
|
-
}
|
|
3151
|
-
var staticLogoNode = defineNode({
|
|
3152
|
-
id: "static_logo",
|
|
3153
|
-
version: "1.4.0",
|
|
3154
|
-
category: "image",
|
|
3155
|
-
location: "local",
|
|
3156
|
-
summary: "Place the brand's own logo file on one delivered size of a static ad, pixel for pixel, never drawn by a model, only where a designer sets one: a top corner or the top centre just inside the safe area, a bottom corner at the safe area's foot, or beside the button on its row (`chooseLogoSpot`, the same function the size's brief reserved its slot with). It keeps the reserved slot (`inputs.slot`) while it is still clean; else it takes the first such place whose whole box (logo, clear space, plate) is off every words box, keeps its clear space off every person, face and product, and has no straight edge (a card's, a window's, a phone's) through it, on plain ground where the logo reads, else smaller, else on a small plate in a brand colour. With no clean place it stamps nothing and flags the size (`no_room`) for its words to be set again with the corner kept clear. Of the logo's files (`inputs.logos`, the same artwork in its colours) it places the one that reads best. Then checks the size: the logo and the headline each wholly inside the frame and the safe area, no words under the logo, and the logo legible (`facts.problems`, blocking in the review).",
|
|
3157
|
-
when_to_use: "Scaffolded by `baker canvas scaffold-static-ad` as the last step of every size (`logo_<size>`, role `logo_composite`) when the brand has a logo file. Free, besides two box-reader calls, the words and the people and products (about $0.01, carried on the run record's review spend).",
|
|
3158
|
-
inputs: z5.object({ image: ImageRef, logos: z5.array(ImageRef).min(1), slot: Rect2.optional() }).strict(),
|
|
3159
|
-
params: LogoParams,
|
|
3160
|
-
outputs: z5.object({ image: ImageRef, facts: LogoFacts, usage: z5.array(Usage2).optional() }).strict(),
|
|
3161
|
-
outputKinds: { image: "image" },
|
|
3162
|
-
cost: () => ({ credits: 0, seconds_estimate: 5 }),
|
|
3163
|
-
async execute({ inputs, params, ctx }) {
|
|
3164
|
-
const { bytes, size } = await imageOf(inputs.image, "static_logo");
|
|
3165
|
-
const files = await Promise.all(inputs.logos.map(logoFileOf));
|
|
3166
|
-
const candidates = await candidatesOf(files, params.inks);
|
|
3167
|
-
const arts = candidates.map((c) => c.art);
|
|
3168
|
-
const position = params.position ?? "top_left";
|
|
3169
|
-
const safe = blockingArea(params.format, params.placements);
|
|
3170
|
-
const aspect = params.aspect ?? arts[0].width / arts[0].height;
|
|
3171
|
-
const copy = params.copy ?? [];
|
|
3172
|
-
const { read, usage } = await wordsBefore(bytes, copy.length > 0 || !inputs.slot);
|
|
3173
|
-
const inks = arts.map((art) => inkLuma(art.data));
|
|
3174
|
-
const where = await whereLogoGoes(
|
|
3175
|
-
{ bytes, size, safe, aspect, position, words: wordsOf(read, copy, size), usage },
|
|
3176
|
-
{ reserved: inputs.slot, arts, inks, plates: params.plates }
|
|
3177
|
-
);
|
|
3178
|
-
const { image, table, home, plate } = where;
|
|
3179
|
-
const fitted = await Promise.all(arts.map((art) => fitArtwork(art, home)));
|
|
3180
|
-
const grounds = fitted.map((art) => groundUnder(image, art, placeInSlot(home, art, where.at)));
|
|
3181
|
-
const shares = fitted.map((art, i) => legibleShare(table, art, placeInSlot(home, art, where.at), inks[i] ?? null));
|
|
3182
|
-
const chosen = plate ? plate.variant : pickLogoVariant(shares);
|
|
3183
|
-
const placed2 = fitted[chosen];
|
|
3184
|
-
const { file: variant, ink } = candidates[chosen];
|
|
3185
|
-
const at = placeInSlot(home, placed2, where.at);
|
|
3186
|
-
const box = { x: at.x, y: at.y, width: placed2.width, height: placed2.height };
|
|
3187
|
-
const out = where.noRoom ? await sharp5(bytes).png().toBuffer() : await sharp5(bytes).composite(await layersOf(placed2, at, plate)).png().toBuffer();
|
|
3188
|
-
const legible = plate ? 1 : shares[chosen] ?? 1;
|
|
3189
|
-
const ground = plate ? plate.luma : grounds[chosen] ?? 255;
|
|
3190
|
-
const { headline, problems } = checkSize(
|
|
3191
|
-
read,
|
|
3192
|
-
{ size, safe, box, legible, plated: plate !== null, noRoom: where.noRoom },
|
|
3193
|
-
copy
|
|
3194
|
-
);
|
|
3195
|
-
const facts = {
|
|
3196
|
-
format: params.format,
|
|
3197
|
-
box,
|
|
3198
|
-
slot: home,
|
|
3199
|
-
variant,
|
|
3200
|
-
...ink ? { ink } : {},
|
|
3201
|
-
...plate ? { plate: { colour: plate.colour } } : {},
|
|
3202
|
-
ground: Math.round(ground),
|
|
3203
|
-
contrast: Math.round(contrastRatio(ground, inks[chosen] ?? 0) * 10) / 10,
|
|
3204
|
-
legible: Math.round(legible * 100) / 100,
|
|
3205
|
-
headline,
|
|
3206
|
-
read: read !== null && copy.length > 0,
|
|
3207
|
-
problems
|
|
3208
|
-
};
|
|
3209
|
-
ctx.log(logLine(facts, files.length));
|
|
3210
|
-
const ingested = await ctx.assets.ingestBytes({ bytes: out, kind: "image", mime: "image/png" });
|
|
3211
|
-
return { image: ingested, facts, ...usage.length > 0 ? { usage } : {} };
|
|
3212
|
-
}
|
|
3213
|
-
});
|
|
3214
|
-
function logLine(facts, files) {
|
|
3215
|
-
const { box, ink, plate, problems } = facts;
|
|
3216
|
-
const found = problems.length > 0 ? `; ${problems.map((p) => `${p.element} ${p.defect}`).join(", ")}` : "";
|
|
3217
|
-
return `${facts.format}: logo file ${facts.variant + 1} of ${files}${ink ? ` in ${ink}` : ""}${plate ? ` on a ${plate.colour} plate` : ""} placed at ${box.x},${box.y} ${box.width}x${box.height} (contrast ${facts.contrast}, legible ${facts.legible})${found}`;
|
|
3218
|
-
}
|
|
3219
|
-
async function whereLogoGoes(at, logo) {
|
|
3220
|
-
const { bytes, size, safe, aspect, position } = at;
|
|
3221
|
-
const picture = await sharp5(bytes).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
|
|
3222
|
-
const image = { data: picture.data, width: picture.info.width, height: picture.info.height };
|
|
3223
|
-
const table = greyTable(image);
|
|
3224
|
-
const planned = await Promise.all(logo.arts.map((art) => fitArtwork(art, logoBoxOn(size, aspect))));
|
|
3225
|
-
const legible = (rect) => planned.some(
|
|
3226
|
-
(art, i) => legibleShare(table, art, placeInSlot(rect, art, position), logo.inks[i] ?? null, 2) >= MIN_LEGIBLE_SHARE
|
|
3227
|
-
);
|
|
3228
|
-
const subjects = await readSubjects(bytes, size, at.usage);
|
|
3229
|
-
const scene = {
|
|
3230
|
-
words: inflatedWords(at.words),
|
|
3231
|
-
subjects: boxesOfKinds(subjects, LOGO_AVOIDS2),
|
|
3232
|
-
cta: buttonOf(at.words),
|
|
3233
|
-
crossesEdge: (r) => straightEdgeIn(table, r),
|
|
3234
|
-
calm: (r) => spreadUnder(table, r) <= PLAIN_GROUND_SPREAD2,
|
|
3235
|
-
legible
|
|
3236
|
-
};
|
|
3237
|
-
const reserved = logo.reserved ? slotAround(logo.reserved, position) : null;
|
|
3238
|
-
if (reserved && logoSpotClean(reserved, scene) && scene.calm?.(reserved.clear) && legible(reserved.rect)) {
|
|
3239
|
-
return { image, table, home: reserved.rect, at: position, plate: null, noRoom: false };
|
|
3240
|
-
}
|
|
3241
|
-
const spot = chooseLogoSpot(size, safe, { aspect, position, ...scene });
|
|
3242
|
-
const plate = spot?.plate ? plateOf(spot.slot.clear, spot.slot.rect, logo) : null;
|
|
3243
|
-
if (spot && (!spot.plate || plate)) {
|
|
3244
|
-
return { image, table, home: spot.slot.rect, at: spot.slot.position, plate, noRoom: false };
|
|
3245
|
-
}
|
|
3246
|
-
const corner = reserved ?? designerSpots(size, safe, { aspect, position })[0];
|
|
3247
|
-
return { image, table, home: corner.rect, at: corner.position, plate: null, noRoom: true };
|
|
3248
|
-
}
|
|
3249
|
-
function plateOf(clear, rect, from) {
|
|
3250
|
-
const chosen = plateColour(plateOptions(from.plates), from.inks);
|
|
3251
|
-
if (!chosen) return null;
|
|
3252
|
-
const luma = hexLuma(chosen.colour) ?? 0;
|
|
3253
|
-
return {
|
|
3254
|
-
rect: clear,
|
|
3255
|
-
radius: Math.round(rect.height * PLATE_RADIUS),
|
|
3256
|
-
colour: chosen.colour,
|
|
3257
|
-
luma,
|
|
3258
|
-
variant: chosen.variant
|
|
3259
|
-
};
|
|
3260
|
-
}
|
|
3261
|
-
var PLATE_RADIUS = 0.25;
|
|
3262
|
-
function buttonOf(words) {
|
|
3263
|
-
const boxes = words.filter((w) => w.cta).map((w) => w.box);
|
|
3264
|
-
if (boxes.length === 0) return null;
|
|
3265
|
-
const x = Math.min(...boxes.map((b) => b.x));
|
|
3266
|
-
const y = Math.min(...boxes.map((b) => b.y));
|
|
3267
|
-
const right = Math.max(...boxes.map((b) => b.x + b.width));
|
|
3268
|
-
const bottom = Math.max(...boxes.map((b) => b.y + b.height));
|
|
3269
|
-
return { x, y, width: right - x, height: bottom - y };
|
|
3270
|
-
}
|
|
3271
|
-
async function layersOf(placed2, at, plate) {
|
|
3272
|
-
const art = await sharp5(placed2.data, { raw: { width: placed2.width, height: placed2.height, channels: 4 } }).png().toBuffer();
|
|
3273
|
-
const layers = [{ input: art, left: at.x, top: at.y }];
|
|
3274
|
-
if (!plate) return layers;
|
|
3275
|
-
const { rect, radius, colour } = plate;
|
|
3276
|
-
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${rect.width}" height="${rect.height}"><rect width="${rect.width}" height="${rect.height}" rx="${radius}" ry="${radius}" fill="${colour}"/></svg>`;
|
|
3277
|
-
return [{ input: Buffer.from(svg), left: rect.x, top: rect.y }, ...layers];
|
|
3278
|
-
}
|
|
3279
|
-
async function wordsBefore(bytes, wanted) {
|
|
3280
|
-
const usage = [];
|
|
3281
|
-
if (!wanted) return { read: null, usage };
|
|
3282
|
-
const reading = await readTextBoxes(bytes, { onSpend: (u) => usage.push(u) });
|
|
3283
|
-
return { read: reading.status === "read" ? reading.boxes : null, usage };
|
|
3284
|
-
}
|
|
3285
|
-
function wordsOf(read, copy, size) {
|
|
3286
|
-
const boxes = read ?? [];
|
|
3287
|
-
const buttons = new Set(copy.filter((l) => /cta|button/i.test(l.role ?? "")).flatMap((l) => boxesOfLine(l, boxes)));
|
|
3288
|
-
return boxes.map((b) => ({ box: pxOf(b, size), cta: buttons.has(b) }));
|
|
3289
|
-
}
|
|
3290
|
-
var LOGO_AVOIDS2 = /* @__PURE__ */ new Set(["person", "face", "product", "animal"]);
|
|
3291
|
-
var PLAIN_GROUND_SPREAD2 = 18;
|
|
3292
|
-
async function readSubjects(bytes, size, usage) {
|
|
3293
|
-
const r = await readSubjectBoxes(bytes, { onSpend: (u) => usage.push(u) });
|
|
3294
|
-
return r.status === "read" ? r.boxes.map((b) => ({ kind: b.kind, box: pxOf(b, size) })) : [];
|
|
3295
|
-
}
|
|
3296
|
-
var boxesOfKinds = (subjects, kinds) => subjects.filter((s) => kinds.has(s.kind)).map((s) => s.box);
|
|
3297
|
-
function plateOptions(hexes) {
|
|
3298
|
-
return (hexes ?? []).flatMap((colour) => {
|
|
3299
|
-
const luma = hexLuma(colour);
|
|
3300
|
-
return luma === null ? [] : [{ colour, luma }];
|
|
3301
|
-
});
|
|
3302
|
-
}
|
|
3303
|
-
function pxOf(b, size) {
|
|
3304
|
-
return {
|
|
3305
|
-
x: Math.round(b.x0 * size.width),
|
|
3306
|
-
y: Math.round(b.y0 * size.height),
|
|
3307
|
-
width: Math.max(1, Math.round((b.x1 - b.x0) * size.width)),
|
|
3308
|
-
height: Math.max(1, Math.round((b.y1 - b.y0) * size.height))
|
|
3309
|
-
};
|
|
3310
|
-
}
|
|
3311
|
-
function checkSize(read, at, copy) {
|
|
3312
|
-
const lines = read && copy.length > 0 ? copyBoxes(read, copy) : null;
|
|
3313
|
-
const headline = lines?.headline ? pxOf(lines.headline, at.size) : null;
|
|
3314
|
-
const inHeadline = (b) => headline !== null && b.x >= headline.x && b.y >= headline.y && b.x + b.width <= headline.x + headline.width && b.y + b.height <= headline.y + headline.height;
|
|
3315
|
-
const words = (read ?? []).filter((b) => b.kind === "text").map((b) => pxOf(b, at.size)).filter((b) => !inHeadline(b));
|
|
3316
|
-
const problems = placementProblems({
|
|
3317
|
-
frame: at.size,
|
|
3318
|
-
safe: at.safe,
|
|
3319
|
-
// With no room nothing was stamped: only the headline is checked.
|
|
3320
|
-
logo: at.noRoom ? null : at.box,
|
|
3321
|
-
headline,
|
|
3322
|
-
copy: (lines?.others ?? []).map((b) => pxOf(b, at.size)),
|
|
3323
|
-
words,
|
|
3324
|
-
legible: at.legible,
|
|
3325
|
-
plated: at.plated
|
|
3326
|
-
});
|
|
3327
|
-
const noRoom = at.noRoom ? [{ element: "logo", defect: "no_room", edges: [] }] : [];
|
|
3328
|
-
return { headline, problems: [...problems, ...noRoom] };
|
|
3329
|
-
}
|
|
3330
|
-
function groundUnder(image, art, at) {
|
|
3331
|
-
let sum = 0;
|
|
3332
|
-
let n = 0;
|
|
3333
|
-
for (let j = 0; j < art.height; j++) {
|
|
3334
|
-
for (let i = 0; i < art.width; i++) {
|
|
3335
|
-
if ((art.data[(j * art.width + i) * 4 + 3] ?? 0) < 128) continue;
|
|
3336
|
-
const u = at.x + i;
|
|
3337
|
-
const v = at.y + j;
|
|
3338
|
-
if (u < 0 || v < 0 || u >= image.width || v >= image.height) continue;
|
|
3339
|
-
const o = (v * image.width + u) * 4;
|
|
3340
|
-
sum += 0.2126 * (image.data[o] ?? 0) + 0.7152 * (image.data[o + 1] ?? 0) + 0.0722 * (image.data[o + 2] ?? 0);
|
|
3341
|
-
n++;
|
|
3342
|
-
}
|
|
3343
|
-
}
|
|
3344
|
-
return n === 0 ? 255 : sum / n;
|
|
3345
|
-
}
|
|
3346
|
-
function copyBoxes(read, copy) {
|
|
3347
|
-
const logoWords = copy.filter((l) => /logo/i.test(l.role ?? "")).map((l) => l.text);
|
|
3348
|
-
const boxes = withWordmark(read, logoWords);
|
|
3349
|
-
const lines = copy.filter((l) => !/logo/i.test(l.role ?? ""));
|
|
3350
|
-
const headline = headlineBox(lines, boxes);
|
|
3351
|
-
const all = lines.flatMap((l) => boxesOfLine(l, boxes));
|
|
3352
|
-
const inHeadline = (b) => headline !== null && b.x0 >= headline.x0 && b.y0 >= headline.y0 && b.x1 <= headline.x1 && b.y1 <= headline.y1;
|
|
3353
|
-
return { headline, others: all.filter((b) => !inHeadline(b)) };
|
|
3354
|
-
}
|
|
3355
|
-
|
|
3356
|
-
// src/engine/nodes/local/lib/size-consistency.ts
|
|
3357
|
-
import sharp6 from "sharp";
|
|
3358
|
-
import { z as z6 } from "zod";
|
|
3359
|
-
var SHOWN_AREA = 0.01;
|
|
3360
|
-
var SEEN_AREA = SHOWN_AREA / 3;
|
|
3361
|
-
var SAME_BOX = 0.3;
|
|
3362
|
-
var COUNTED = [
|
|
3363
|
-
{ kind: "person", name: "people" },
|
|
3364
|
-
{ kind: "face", name: "faces" },
|
|
3365
|
-
{ kind: "animal", name: "animals" }
|
|
3366
|
-
];
|
|
3367
|
-
var area = (b) => Math.max(0, b.x1 - b.x0) * Math.max(0, b.y1 - b.y0);
|
|
3368
|
-
function overlap2(a, b) {
|
|
3369
|
-
const ix = Math.max(0, Math.min(a.x1, b.x1) - Math.max(a.x0, b.x0));
|
|
3370
|
-
const iy = Math.max(0, Math.min(a.y1, b.y1) - Math.max(a.y0, b.y0));
|
|
3371
|
-
const union = area(a) + area(b) - ix * iy;
|
|
3372
|
-
return union > 0 ? ix * iy / union : 0;
|
|
3373
|
-
}
|
|
3374
|
-
function surely(image, kind) {
|
|
3375
|
-
const [first = [], ...rest] = image.reads.map((r) => r.filter((b) => b.kind === kind));
|
|
3376
|
-
return first.filter((b) => area(b) >= SHOWN_AREA && rest.every((r) => r.some((o) => overlap2(b, o) >= SAME_BOX)));
|
|
3377
|
-
}
|
|
3378
|
-
function atMost(image, kind) {
|
|
3379
|
-
return Math.max(0, ...image.reads.map((r) => r.filter((b) => b.kind === kind && area(b) >= SEEN_AREA).length));
|
|
3380
|
-
}
|
|
3381
|
-
function boxDifferences(master, size) {
|
|
3382
|
-
return COUNTED.flatMap(({ kind, name }) => {
|
|
3383
|
-
const shown = surely(size, kind).length;
|
|
3384
|
-
const shownBefore = atMost(master, kind);
|
|
3385
|
-
if (shown > shownBefore) {
|
|
3386
|
-
return [{ kind: "added", what: `shows ${shown} ${name} where the main size shows ${shownBefore}` }];
|
|
3387
|
-
}
|
|
3388
|
-
const before = surely(master, kind).length;
|
|
3389
|
-
const now = atMost(size, kind);
|
|
3390
|
-
if (before > now) return [{ kind: "missing", what: `shows ${now} ${name} where the main size shows ${before}` }];
|
|
3391
|
-
return [];
|
|
3392
|
-
});
|
|
3393
|
-
}
|
|
3394
|
-
var PAINTED = 0.6;
|
|
3395
|
-
function paintedSubjects(plate, paintedShare) {
|
|
3396
|
-
const words = (label) => label.toLowerCase().match(new RegExp("\\p{L}{3,}", "gu")) ?? [];
|
|
3397
|
-
const keptProducts = new Set(
|
|
3398
|
-
surely(plate, "product").filter((b) => paintedShare(b) < PAINTED).flatMap((b) => words(b.label))
|
|
3399
|
-
);
|
|
3400
|
-
return ["person", "face", "animal", "product"].flatMap(
|
|
3401
|
-
(kind) => surely(plate, kind).filter((b) => paintedShare(b) >= PAINTED).filter((b) => kind !== "product" || words(b.label).some((w) => keptProducts.has(w))).map((b) => b.label)
|
|
3402
|
-
);
|
|
3403
|
-
}
|
|
3404
|
-
var SIZE_MATCH_PROMPT = [
|
|
3405
|
-
"Image 1 is the main size of one static ad. Image 2 is the same ad made in another size. Image 2 may show more or less of the scene and lay the words out differently: that is expected and never a finding. Compare only the people, animals and products the two pictures show:",
|
|
3406
|
-
"- people: Image 2 shows a different number of the ad's people than Image 1, or a different person where Image 1 has one (another face, age, gender or clothing).",
|
|
3407
|
-
"- product: the product the ad is about (the thing sold or shown in use) is a different object in Image 2 (another model, shape, colour or label), or is missing from it.",
|
|
3408
|
-
"- duplicated: Image 2 shows the same person, animal or product twice where Image 1 shows it once.",
|
|
3409
|
-
"- stretched: a person, face or product in Image 2 is visibly stretched or squashed compared with Image 1 (wider or taller than it is).",
|
|
3410
|
-
"- framed: Image 2 holds Image 1's picture as a smaller rectangle inside a bigger frame, with a visible edge, border or seam around it.",
|
|
3411
|
-
"Someone in the far background, a crowd, and a prop a bigger frame shows more of are never findings. Say only what you see clearly at phone size; when unsure, it is the same.",
|
|
3412
|
-
'Reply with only this JSON: {"people":"same|different","product":"same|different|missing|none","duplicated":"<what is shown twice>|null","stretched":"<who or what>|null","framed":true|false,"what":"<one short sentence naming what differs, or empty>"}'
|
|
3413
|
-
].join("\n");
|
|
3414
|
-
var SizeMatchAnswer = z6.object({
|
|
3415
|
-
people: z6.enum(["same", "different"]),
|
|
3416
|
-
product: z6.enum(["same", "different", "missing", "none"]),
|
|
3417
|
-
duplicated: z6.string().nullable().optional(),
|
|
3418
|
-
stretched: z6.string().nullable().optional(),
|
|
3419
|
-
framed: z6.boolean()
|
|
3420
|
-
});
|
|
3421
|
-
var named = (text) => {
|
|
3422
|
-
const t = text?.trim();
|
|
3423
|
-
return t && t.toLowerCase() !== "null" ? t : null;
|
|
3424
|
-
};
|
|
3425
|
-
function sizeMatchFromAnswer(answer) {
|
|
3426
|
-
const parsed = SizeMatchAnswer.safeParse(answer);
|
|
3427
|
-
if (!parsed.success) return null;
|
|
3428
|
-
const a = parsed.data;
|
|
3429
|
-
const twice = named(a.duplicated);
|
|
3430
|
-
const stretched = named(a.stretched);
|
|
3431
|
-
return [
|
|
3432
|
-
...a.people === "different" ? [{ kind: "other_people", what: "shows other people than the main size" }] : [],
|
|
3433
|
-
...a.product === "different" ? [{ kind: "other_product", what: "shows another product than the main size" }] : a.product === "missing" ? [{ kind: "no_product", what: "no longer shows the product the main size shows" }] : [],
|
|
3434
|
-
...twice ? [{ kind: "duplicated", what: `shows ${twice} twice` }] : [],
|
|
3435
|
-
...stretched ? [{ kind: "stretched", what: `shows ${stretched} stretched or squashed` }] : [],
|
|
3436
|
-
...a.framed ? [{ kind: "framed", what: "shows the main size's picture boxed inside a bigger frame" }] : []
|
|
3437
|
-
];
|
|
3438
|
-
}
|
|
3439
|
-
var READS = 2;
|
|
3440
|
-
var MATCH_EDGE = 1024;
|
|
3441
|
-
async function readTwice(bytes, onSpend) {
|
|
3442
|
-
const meta = await sharp6(bytes).metadata();
|
|
3443
|
-
const reads = await Promise.all(Array.from({ length: READS }, () => readSubjectBoxes(bytes, { onSpend })));
|
|
3444
|
-
const boxes = reads.flatMap((r) => r.status === "read" ? [r.boxes] : []);
|
|
3445
|
-
return boxes.length === READS ? { size: { width: meta.width ?? 0, height: meta.height ?? 0 }, reads: boxes } : null;
|
|
3446
|
-
}
|
|
3447
|
-
var matchJpeg = async (bytes) => (await sharp6(bytes).resize(MATCH_EDGE, MATCH_EDGE, { fit: "inside", withoutEnlargement: true }).jpeg({ quality: 88 }).toBuffer()).toString("base64");
|
|
3448
|
-
var unique2 = (diffs) => diffs.filter((d, i) => diffs.findIndex((o) => o.what === d.what) === i);
|
|
3449
|
-
async function compareSizes(master, sizes, opts = {}) {
|
|
3450
|
-
const masterBytes = await bytesOf(master.path);
|
|
3451
|
-
const [masterReads, masterJpeg] = await Promise.all([readTwice(masterBytes, opts.onSpend), matchJpeg(masterBytes)]);
|
|
3452
|
-
return await Promise.all(
|
|
3453
|
-
sizes.map(async (size) => {
|
|
3454
|
-
const bytes = await bytesOf(size.path);
|
|
3455
|
-
const [reads, answer] = await Promise.all([
|
|
3456
|
-
readTwice(bytes, opts.onSpend),
|
|
3457
|
-
matchJpeg(bytes).then(
|
|
3458
|
-
(jpeg) => askAboutImages(SIZE_MATCH_MODEL, [masterJpeg, jpeg], SIZE_MATCH_PROMPT, opts.onSpend, 6e4, {
|
|
3459
|
-
effort: "low"
|
|
3460
|
-
})
|
|
3461
|
-
)
|
|
3462
|
-
]);
|
|
3463
|
-
const boxed = masterReads && reads ? boxDifferences(masterReads, reads) : null;
|
|
3464
|
-
const seen = sizeMatchFromAnswer(answer);
|
|
3465
|
-
return {
|
|
3466
|
-
format: size.format,
|
|
3467
|
-
compared: boxed !== null || seen !== null,
|
|
3468
|
-
differences: unique2([...boxed ?? [], ...seen ?? []])
|
|
3469
|
-
};
|
|
3470
|
-
})
|
|
3471
|
-
);
|
|
3472
|
-
}
|
|
3473
|
-
function sizeDifferenceFinding(format, differences) {
|
|
3474
|
-
const missing = differences.some((d) => d.kind === "missing" || d.kind === "no_product");
|
|
3475
|
-
return {
|
|
3476
|
-
severity: "blocking",
|
|
3477
|
-
what: `${format}: ${differences.map((d) => d.what).join("; ")}. It must show the main size's people and product, once each, at their own proportions.`,
|
|
3478
|
-
kind: { item: "sizes", element: "picture", defect: missing ? "missing" : "other", edges: [] }
|
|
3479
|
-
};
|
|
3480
|
-
}
|
|
3481
|
-
|
|
3482
|
-
export {
|
|
3483
|
-
SubjectBoxSchema,
|
|
3484
|
-
paintedSubjects,
|
|
3485
|
-
readTwice,
|
|
3486
|
-
compareSizes,
|
|
3487
|
-
sizeDifferenceFinding,
|
|
3488
|
-
registryParamShape,
|
|
3489
|
-
imageGenerateNode,
|
|
3490
|
-
timedStep,
|
|
3491
|
-
isRead,
|
|
3492
|
-
subjectsStep,
|
|
3493
|
-
imageOf,
|
|
3494
|
-
rawOf,
|
|
3495
|
-
pngOf,
|
|
3496
|
-
subjectCrop,
|
|
3497
|
-
boxesToPlace,
|
|
3498
|
-
planSafeFit,
|
|
3499
|
-
plainEdge,
|
|
3500
|
-
minFitScale,
|
|
3501
|
-
fitPicture,
|
|
3502
|
-
registerToSource,
|
|
3503
|
-
warpToCut,
|
|
3504
|
-
MISALIGNED,
|
|
3505
|
-
staticFormatNode,
|
|
3506
|
-
splitWordBoxes,
|
|
3507
|
-
LogoFacts,
|
|
3508
|
-
staticLogoNode
|
|
3509
|
-
};
|
|
3510
|
-
//# sourceMappingURL=chunk-AH6TDWYS.js.map
|