fritzkit 0.0.0-stage → 0.1.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/.claude-plugin/marketplace.json +12 -0
- package/.claude-plugin/plugin.json +13 -0
- package/CHANGELOG.md +8 -0
- package/LICENSE +21 -0
- package/README.md +429 -2
- package/dist/cli/commands/doctor.d.ts +4 -0
- package/dist/cli/commands/doctor.js +7 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/export.d.ts +17 -0
- package/dist/cli/commands/export.js +81 -0
- package/dist/cli/commands/export.js.map +1 -0
- package/dist/cli/commands/kicad.d.ts +10 -0
- package/dist/cli/commands/kicad.js +22 -0
- package/dist/cli/commands/kicad.js.map +1 -0
- package/dist/cli/commands/parts.d.ts +22 -0
- package/dist/cli/commands/parts.js +46 -0
- package/dist/cli/commands/parts.js.map +1 -0
- package/dist/cli/commands/prune.d.ts +14 -0
- package/dist/cli/commands/prune.js +67 -0
- package/dist/cli/commands/prune.js.map +1 -0
- package/dist/cli/commands/render.d.ts +24 -0
- package/dist/cli/commands/render.js +62 -0
- package/dist/cli/commands/render.js.map +1 -0
- package/dist/cli/commands/schema.d.ts +10 -0
- package/dist/cli/commands/schema.js +35 -0
- package/dist/cli/commands/schema.js.map +1 -0
- package/dist/cli/commands/ui.d.ts +14 -0
- package/dist/cli/commands/ui.js +67 -0
- package/dist/cli/commands/ui.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +24 -0
- package/dist/cli/commands/validate.js +91 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/context.d.ts +81 -0
- package/dist/cli/context.js +139 -0
- package/dist/cli/context.js.map +1 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +8 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/program.d.ts +21 -0
- package/dist/cli/program.js +158 -0
- package/dist/cli/program.js.map +1 -0
- package/dist/cli/raster.d.ts +4 -0
- package/dist/cli/raster.js +9 -0
- package/dist/cli/raster.js.map +1 -0
- package/dist/core/compile/breadboard-util.d.ts +12 -0
- package/dist/core/compile/breadboard-util.js +107 -0
- package/dist/core/compile/breadboard-util.js.map +1 -0
- package/dist/core/compile/breadboard.d.ts +60 -0
- package/dist/core/compile/breadboard.js +513 -0
- package/dist/core/compile/breadboard.js.map +1 -0
- package/dist/core/compile/fzp.d.ts +12 -0
- package/dist/core/compile/fzp.js +183 -0
- package/dist/core/compile/fzp.js.map +1 -0
- package/dist/core/compile/icon.d.ts +17 -0
- package/dist/core/compile/icon.js +27 -0
- package/dist/core/compile/icon.js.map +1 -0
- package/dist/core/compile/index.d.ts +4 -0
- package/dist/core/compile/index.js +62 -0
- package/dist/core/compile/index.js.map +1 -0
- package/dist/core/compile/pcb.d.ts +39 -0
- package/dist/core/compile/pcb.js +164 -0
- package/dist/core/compile/pcb.js.map +1 -0
- package/dist/core/compile/schematic.d.ts +69 -0
- package/dist/core/compile/schematic.js +240 -0
- package/dist/core/compile/schematic.js.map +1 -0
- package/dist/core/compile/style.d.ts +52 -0
- package/dist/core/compile/style.js +72 -0
- package/dist/core/compile/style.js.map +1 -0
- package/dist/core/compile/types.d.ts +50 -0
- package/dist/core/compile/types.js +15 -0
- package/dist/core/compile/types.js.map +1 -0
- package/dist/core/export/bin.d.ts +61 -0
- package/dist/core/export/bin.js +221 -0
- package/dist/core/export/bin.js.map +1 -0
- package/dist/core/export/fzpz.d.ts +37 -0
- package/dist/core/export/fzpz.js +121 -0
- package/dist/core/export/fzpz.js.map +1 -0
- package/dist/core/export/index.d.ts +5 -0
- package/dist/core/export/index.js +6 -0
- package/dist/core/export/index.js.map +1 -0
- package/dist/core/export/install.d.ts +77 -0
- package/dist/core/export/install.js +183 -0
- package/dist/core/export/install.js.map +1 -0
- package/dist/core/export/prune.d.ts +72 -0
- package/dist/core/export/prune.js +165 -0
- package/dist/core/export/prune.js.map +1 -0
- package/dist/core/fritzing.d.ts +22 -0
- package/dist/core/fritzing.js +77 -0
- package/dist/core/fritzing.js.map +1 -0
- package/dist/core/identity.d.ts +29 -0
- package/dist/core/identity.js +70 -0
- package/dist/core/identity.js.map +1 -0
- package/dist/core/image/bbox.d.ts +27 -0
- package/dist/core/image/bbox.js +24 -0
- package/dist/core/image/bbox.js.map +1 -0
- package/dist/core/image/index.d.ts +8 -0
- package/dist/core/image/index.js +9 -0
- package/dist/core/image/index.js.map +1 -0
- package/dist/core/image/key.d.ts +72 -0
- package/dist/core/image/key.js +317 -0
- package/dist/core/image/key.js.map +1 -0
- package/dist/core/image/prepare.d.ts +31 -0
- package/dist/core/image/prepare.js +98 -0
- package/dist/core/image/prepare.js.map +1 -0
- package/dist/core/image/remover.d.ts +17 -0
- package/dist/core/image/remover.js +51 -0
- package/dist/core/image/remover.js.map +1 -0
- package/dist/core/index.d.ts +16 -0
- package/dist/core/index.js +17 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/kicad/footprint.d.ts +104 -0
- package/dist/core/kicad/footprint.js +317 -0
- package/dist/core/kicad/footprint.js.map +1 -0
- package/dist/core/kicad/index.d.ts +25 -0
- package/dist/core/kicad/index.js +197 -0
- package/dist/core/kicad/index.js.map +1 -0
- package/dist/core/kicad/sexpr.d.ts +35 -0
- package/dist/core/kicad/sexpr.js +163 -0
- package/dist/core/kicad/sexpr.js.map +1 -0
- package/dist/core/resolve.d.ts +113 -0
- package/dist/core/resolve.js +301 -0
- package/dist/core/resolve.js.map +1 -0
- package/dist/core/schema/part.d.ts +995 -0
- package/dist/core/schema/part.js +278 -0
- package/dist/core/schema/part.js.map +1 -0
- package/dist/core/schema/part.schema.json +1122 -0
- package/dist/core/svg.d.ts +20 -0
- package/dist/core/svg.js +56 -0
- package/dist/core/svg.js.map +1 -0
- package/dist/core/units.d.ts +28 -0
- package/dist/core/units.js +42 -0
- package/dist/core/units.js.map +1 -0
- package/dist/core/validate/checker.d.ts +46 -0
- package/dist/core/validate/checker.js +142 -0
- package/dist/core/validate/checker.js.map +1 -0
- package/dist/core/validate/doctor.d.ts +25 -0
- package/dist/core/validate/doctor.js +156 -0
- package/dist/core/validate/doctor.js.map +1 -0
- package/dist/core/validate/explain.d.ts +10 -0
- package/dist/core/validate/explain.js +135 -0
- package/dist/core/validate/explain.js.map +1 -0
- package/dist/core/validate/index.d.ts +21 -0
- package/dist/core/validate/index.js +96 -0
- package/dist/core/validate/index.js.map +1 -0
- package/dist/core/validate/structural.d.ts +74 -0
- package/dist/core/validate/structural.js +664 -0
- package/dist/core/validate/structural.js.map +1 -0
- package/dist/core/workspace.d.ts +69 -0
- package/dist/core/workspace.js +213 -0
- package/dist/core/workspace.js.map +1 -0
- package/dist/mcp/jsonpatch.d.ts +30 -0
- package/dist/mcp/jsonpatch.js +145 -0
- package/dist/mcp/jsonpatch.js.map +1 -0
- package/dist/mcp/main.d.ts +2 -0
- package/dist/mcp/main.js +5 -0
- package/dist/mcp/main.js.map +1 -0
- package/dist/mcp/server.d.ts +28 -0
- package/dist/mcp/server.js +310 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/server/api.d.ts +72 -0
- package/dist/server/api.js +507 -0
- package/dist/server/api.js.map +1 -0
- package/dist/server/events.d.ts +28 -0
- package/dist/server/events.js +134 -0
- package/dist/server/events.js.map +1 -0
- package/dist/server/http.d.ts +36 -0
- package/dist/server/http.js +127 -0
- package/dist/server/http.js.map +1 -0
- package/dist/server/index.d.ts +65 -0
- package/dist/server/index.js +193 -0
- package/dist/server/index.js.map +1 -0
- package/dist/ui/assets/index-DLU8opyI.js +63 -0
- package/dist/ui/assets/index-GhUA-T3b.css +1 -0
- package/dist/ui/index.html +14 -0
- package/package.json +89 -4
- package/skills/fritzkit/SKILL.md +690 -0
- package/skills/fritzkit/reference/examples.md +275 -0
- package/skills/fritzkit/reference/part-json.md +333 -0
- package/skills/fritzkit/scripts/measure.py +197 -0
- package/tools/fzp-checker/.python-version +1 -0
- package/tools/fzp-checker/README.md +45 -0
- package/tools/fzp-checker/check_fzp.py +48 -0
- package/tools/fzp-checker/pyproject.toml +26 -0
- package/tools/fzp-checker/uv.lock +266 -0
- package/tools/fzp-checker/vendor/LICENSE.txt +8 -0
- package/tools/fzp-checker/vendor/UPSTREAM.md +25 -0
- package/tools/fzp-checker/vendor/fzp_checker.py +14 -0
- package/tools/fzp-checker/vendor/scripts/checks/__init__.py +26 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/__init__.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/check_missing_leg_ids.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/connector_numbering.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/fzp_checker_runner.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/fzp_checkers.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/fzp_svg_checkers.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/fzp_utils.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/svg_checkers.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/svg_utils.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/check_missing_leg_ids.py +44 -0
- package/tools/fzp-checker/vendor/scripts/checks/checkascii.py +27 -0
- package/tools/fzp-checker/vendor/scripts/checks/checkcase.py +156 -0
- package/tools/fzp-checker/vendor/scripts/checks/checkcopies.py +104 -0
- package/tools/fzp-checker/vendor/scripts/checks/connector_numbering.py +79 -0
- package/tools/fzp-checker/vendor/scripts/checks/connectors_misnumbered.py +228 -0
- package/tools/fzp-checker/vendor/scripts/checks/explain_errors.md +95 -0
- package/tools/fzp-checker/vendor/scripts/checks/fzp_checker_runner.py +717 -0
- package/tools/fzp-checker/vendor/scripts/checks/fzp_checkers.py +1069 -0
- package/tools/fzp-checker/vendor/scripts/checks/fzp_svg_checkers.py +62 -0
- package/tools/fzp-checker/vendor/scripts/checks/fzp_utils.py +222 -0
- package/tools/fzp-checker/vendor/scripts/checks/inspector_fc51.png +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/invisibleconnectors.py +118 -0
- package/tools/fzp-checker/vendor/scripts/checks/nolayeridcheck.txt +758 -0
- package/tools/fzp-checker/vendor/scripts/checks/requirements.txt +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/svg_checkers.py +953 -0
- package/tools/fzp-checker/vendor/scripts/checks/svg_utils.py +96 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_checkers.py +839 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/author_missing.fzp.test +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/author_present.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/buses_missing_id.fzp.test +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/buses_missing_node_members.fzp.test +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/buses_present_valid.fzp.test +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_layers_missing_attributes.fzp.test +16 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_layers_present_valid.fzp.test +14 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_numbering_invalid.fzp.test +70 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_numbering_valid.fzp.test +70 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_refs_valid.fzp.test +53 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_mixed.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_silkscreen_nested.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_style_attr.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_white_stroke.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_wrong_color.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_inherited.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_legacy.fzp.test +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_nested_copper.fzp.test +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_none.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_standard.fzp.test +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_uppercase.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/css_connector.fzp.test +100 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/description_missing.fzp.test +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/description_present.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/duplicate_ids.fzp.test +27 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/family_property_missing.fzp.test +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/family_property_present_valid.fzp.test +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/font_size.fzp.test +46 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/fritzing_version_invalid_format.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/fritzing_version_missing.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/fritzing_version_present_valid.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/gorn_absent.fzp.test +15 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/gorn_present.fzp.test +15 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/hybrid_connectors.fzp.test +232 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/invalid_xml.fzp.test +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_ids_match.fzp.test +29 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_ids_mismatch.fzp.test +30 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_nesting_invalid.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_nesting_valid.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/matrix_transform.fzp.test +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/missing_connector_refs.fzp.test +63 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/missing_leg_ids.fzp.test +34 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_missing.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_non_alnum.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_present.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_special_chars_absent.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_special_chars_present.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_too_short.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/no_layer_invalid.fzp.test +19 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/no_layer_namedview.fzp.test +19 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/no_layer_valid.fzp.test +19 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/pcb_only.fzp.test +48 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/property_fields_missing_name.fzp.test +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/property_fields_missing_value.fzp.test +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/property_fields_present.fzp.test +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/required_tags_missing.fzp.test +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/required_tags_present.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/stroke_test.fzp.test +60 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/template_svgs.fzp.test +35 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/title_missing.fzp.test +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/title_present.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/unique_ids.fzp.test +27 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/unique_property_names_duplicates.fzp.test +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/unique_property_names_unique.fzp.test +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/valid_xml.fzp.test +249 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/version_invalid_format.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/version_missing.fzp.test +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/version_present_valid.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/views_missing.fzp.test +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/views_present.fzp.test +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/connector_refs_valid_breadboard.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/duplicate_ids_breadboard.svg +13 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/gorn_absent_breadboard.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/gorn_present_breadboard.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/hybrid_connectors_breadboard.svg +102 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/layer_nesting_invalid.svg +13 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/layer_nesting_valid.svg +13 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/matrix_transform.svg +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/missing_connector_refs_breadboard.svg +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/missing_leg_ids_breadboard.svg +9 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/test_layer_ids_match_breadboard.svg +9 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/test_layer_ids_mismatch_breadboard.svg +9 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/unique_ids_breadboard.svg +13 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/hybrid_connectors_icon.svg +78 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/no_layer_invalid.svg +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/no_layer_namedview.svg +9 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/no_layer_valid.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/plain_pcb.svg +10 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/connector_refs_valid_pcb.svg +10 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_mixed.svg +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_silkscreen_nested.svg +10 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_style_attr.svg +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_white_stroke.svg +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_wrong_color.svg +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_inherited.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_legacy.svg +10 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_nested_copper.svg +9 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_none.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_standard.svg +10 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_uppercase.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/css_connector.svg +25 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/font_size.svg +54 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/hybrid_connectors_pcb.svg +35 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/missing_connector_refs_pcb.svg +14 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/pcb-arduino-shield.svg +25 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/stroke_test.svg +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/test_layer_ids_match_pcb.svg +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/test_layer_ids_mismatch_pcb.svg +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/connector_refs_valid_schematic.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/hybrid_connectors_schematic.svg +59 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/missing_connector_refs_schematic.svg +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/test_layer_ids_match_schematic.svg +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/test_layer_ids_mismatch_schematic.svg +6 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/core/identity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Photo calibration: map between photo pixels and the part frame (mm, origin at the
|
|
3
|
+
* outline's top-left, x right, y down) given the detected board bbox and the known
|
|
4
|
+
* outline size. Pixel coordinates are continuous: (bbox.x, bbox.y) is the left/top edge
|
|
5
|
+
* of the board and (bbox.x + bbox.w, bbox.y + bbox.h) its right/bottom edge, so the
|
|
6
|
+
* centre of pixel column i is at i + 0.5.
|
|
7
|
+
*/
|
|
8
|
+
import type { PixelBox } from "./key.js";
|
|
9
|
+
export interface OutlineSize {
|
|
10
|
+
widthMm: number;
|
|
11
|
+
heightMm: number;
|
|
12
|
+
}
|
|
13
|
+
/** Pixels per millimetre along each axis (they differ when the photo is not square-on). */
|
|
14
|
+
export declare function pxPerMm(bbox: PixelBox, outline: OutlineSize): {
|
|
15
|
+
x: number;
|
|
16
|
+
y: number;
|
|
17
|
+
};
|
|
18
|
+
/** Photo pixel -> part frame mm. */
|
|
19
|
+
export declare function pxToMm(px: number, py: number, bbox: PixelBox, outline: OutlineSize): {
|
|
20
|
+
xMm: number;
|
|
21
|
+
yMm: number;
|
|
22
|
+
};
|
|
23
|
+
/** Part frame mm -> photo pixel. */
|
|
24
|
+
export declare function mmToPx(xMm: number, yMm: number, bbox: PixelBox, outline: OutlineSize): {
|
|
25
|
+
px: number;
|
|
26
|
+
py: number;
|
|
27
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
function check(bbox, outline) {
|
|
2
|
+
if (!(bbox.w > 0 && bbox.h > 0)) {
|
|
3
|
+
throw new RangeError(`board bbox must have a positive size, got ${bbox.w}x${bbox.h} px`);
|
|
4
|
+
}
|
|
5
|
+
if (!(outline.widthMm > 0 && outline.heightMm > 0)) {
|
|
6
|
+
throw new RangeError(`outline must have a positive size, got ${outline.widthMm}x${outline.heightMm} mm`);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
/** Pixels per millimetre along each axis (they differ when the photo is not square-on). */
|
|
10
|
+
export function pxPerMm(bbox, outline) {
|
|
11
|
+
check(bbox, outline);
|
|
12
|
+
return { x: bbox.w / outline.widthMm, y: bbox.h / outline.heightMm };
|
|
13
|
+
}
|
|
14
|
+
/** Photo pixel -> part frame mm. */
|
|
15
|
+
export function pxToMm(px, py, bbox, outline) {
|
|
16
|
+
const s = pxPerMm(bbox, outline);
|
|
17
|
+
return { xMm: (px - bbox.x) / s.x, yMm: (py - bbox.y) / s.y };
|
|
18
|
+
}
|
|
19
|
+
/** Part frame mm -> photo pixel. */
|
|
20
|
+
export function mmToPx(xMm, yMm, bbox, outline) {
|
|
21
|
+
const s = pxPerMm(bbox, outline);
|
|
22
|
+
return { px: bbox.x + xMm * s.x, py: bbox.y + yMm * s.y };
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=bbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bbox.js","sourceRoot":"","sources":["../../../src/core/image/bbox.ts"],"names":[],"mappings":"AAcA,SAAS,KAAK,CAAC,IAAc,EAAE,OAAoB;IACjD,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,UAAU,CAAC,6CAA6C,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC;IAC3F,CAAC;IACD,IAAI,CAAC,CAAC,OAAO,CAAC,OAAO,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,UAAU,CAClB,0CAA0C,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,QAAQ,KAAK,CACnF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,OAAO,CAAC,IAAc,EAAE,OAAoB;IAC1D,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACrB,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;AACvE,CAAC;AAED,oCAAoC;AACpC,MAAM,UAAU,MAAM,CACpB,EAAU,EACV,EAAU,EACV,IAAc,EACd,OAAoB;IAEpB,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACjC,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;AAChE,CAAC;AAED,oCAAoC;AACpC,MAAM,UAAU,MAAM,CACpB,GAAW,EACX,GAAW,EACX,IAAc,EACd,OAAoB;IAEpB,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACjC,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;AAC5D,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image module (spec decision D19): prepare listing photos for the photo artwork style.
|
|
3
|
+
* Uses sharp; the optional ML remover is loaded only on request.
|
|
4
|
+
*/
|
|
5
|
+
export * from "./key.js";
|
|
6
|
+
export * from "./bbox.js";
|
|
7
|
+
export * from "./prepare.js";
|
|
8
|
+
export * from "./remover.js";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image module (spec decision D19): prepare listing photos for the photo artwork style.
|
|
3
|
+
* Uses sharp; the optional ML remover is loaded only on request.
|
|
4
|
+
*/
|
|
5
|
+
export * from "./key.js";
|
|
6
|
+
export * from "./bbox.js";
|
|
7
|
+
export * from "./prepare.js";
|
|
8
|
+
export * from "./remover.js";
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/image/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/** A pixel rectangle: x/y is the top-left pixel, w/h the size in pixels. */
|
|
2
|
+
export interface PixelBox {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
w: number;
|
|
6
|
+
h: number;
|
|
7
|
+
}
|
|
8
|
+
export interface KeyOptions {
|
|
9
|
+
/** Max per-channel distance (0-255) from the seed colour that still counts as background. Default 28. */
|
|
10
|
+
tolerance?: number;
|
|
11
|
+
/** Soften the cut edge over this many pixels (gaussian sigma). Default 0 (hard edge). */
|
|
12
|
+
feather?: number;
|
|
13
|
+
/**
|
|
14
|
+
* Opaque islands smaller than this many pixels (dust, JPEG speckle in the background)
|
|
15
|
+
* are keyed too. Default max(16, width*height/10000). 0 or 1 disables it.
|
|
16
|
+
*/
|
|
17
|
+
minIslandPx?: number;
|
|
18
|
+
/**
|
|
19
|
+
* Close notches in the board outline (white features touching the board edge) with a
|
|
20
|
+
* morphological closing of the opaque mask, square element of radius
|
|
21
|
+
* round(0.025 * min(bbox w, h)) clamped to 2..24 px, applied only inside the board
|
|
22
|
+
* bbox. Default false.
|
|
23
|
+
*/
|
|
24
|
+
fillNotches?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface KeyResult {
|
|
27
|
+
/** RGBA PNG with the background made transparent. */
|
|
28
|
+
png: Uint8Array;
|
|
29
|
+
/** Bounding box of the remaining opaque pixels (the board); w = h = 0 if none. */
|
|
30
|
+
bbox: PixelBox;
|
|
31
|
+
}
|
|
32
|
+
export interface BoardBbox extends PixelBox {
|
|
33
|
+
/** Size of the analysed image. */
|
|
34
|
+
widthPx: number;
|
|
35
|
+
heightPx: number;
|
|
36
|
+
}
|
|
37
|
+
/** A decoded RGBA image (4 bytes per pixel, row-major). */
|
|
38
|
+
export interface RgbaImage {
|
|
39
|
+
data: Uint8Array;
|
|
40
|
+
width: number;
|
|
41
|
+
height: number;
|
|
42
|
+
}
|
|
43
|
+
export declare const DEFAULT_TOLERANCE = 28;
|
|
44
|
+
/** Decode any sharp-supported image to straight RGBA, applying the EXIF orientation. */
|
|
45
|
+
export declare function decodeRgba(data: Uint8Array): Promise<RgbaImage>;
|
|
46
|
+
/** Encode an RGBA image as PNG (deterministic settings). */
|
|
47
|
+
export declare function encodePng(img: RgbaImage): Promise<Uint8Array>;
|
|
48
|
+
/**
|
|
49
|
+
* Background mask: 1 = background (reached by a fill from a corner/edge seed), 0 = kept.
|
|
50
|
+
* Iterative stack fill with 4-connectivity; each pixel is pushed at most once.
|
|
51
|
+
*/
|
|
52
|
+
export declare function backgroundMask(img: RgbaImage, tolerance?: number): Uint8Array;
|
|
53
|
+
/** Bounding box of the pixels where `keep(p)` is true; w = h = 0 when there are none. */
|
|
54
|
+
export declare function maskBbox(w: number, h: number, keep: (p: number) => boolean): PixelBox;
|
|
55
|
+
/** Bounding box of pixels with alpha >= 128. */
|
|
56
|
+
export declare function alphaBbox(img: RgbaImage): PixelBox;
|
|
57
|
+
/** Key the background of a decoded image. Returns a new RGBA image and the board bbox. */
|
|
58
|
+
export declare function keyRgba(img: RgbaImage, opts?: KeyOptions): Promise<{
|
|
59
|
+
image: RgbaImage;
|
|
60
|
+
bbox: PixelBox;
|
|
61
|
+
}>;
|
|
62
|
+
/**
|
|
63
|
+
* Make the near-white listing background transparent. `data` is any image sharp can
|
|
64
|
+
* decode (PNG, JPEG, WebP, ...). Throws if it cannot be decoded.
|
|
65
|
+
*/
|
|
66
|
+
export declare function keyBackground(data: Uint8Array, opts?: KeyOptions): Promise<KeyResult>;
|
|
67
|
+
/**
|
|
68
|
+
* Detect the board's bounding box (same fill as keyBackground, no output image). The UI
|
|
69
|
+
* uses this for calibration together with bbox.ts. Throws if `data` cannot be decoded.
|
|
70
|
+
*/
|
|
71
|
+
export declare function detectBoardBbox(data: Uint8Array, opts?: KeyOptions): Promise<BoardBbox>;
|
|
72
|
+
export declare function clamp(v: number, lo: number, hi: number): number;
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Background keying for listing photos (spec decision D19, default "key" method).
|
|
3
|
+
*
|
|
4
|
+
* Amazon-style listing photos put the board on a near-white (#F7F7F7..#FFFFFF) or light
|
|
5
|
+
* grey background. We flood fill from the four corners and the four edge midpoints
|
|
6
|
+
* through pixels close to the seed colour and make them transparent. Pixels enclosed by
|
|
7
|
+
* the board are never reached, so white silkscreen and white connectors stay opaque.
|
|
8
|
+
*
|
|
9
|
+
* Known limits:
|
|
10
|
+
* 1. White features that TOUCH the board edge (labels, white headers, connectors) are
|
|
11
|
+
* connected to the background and get keyed, leaving notches. Set
|
|
12
|
+
* `fillNotches: true` to close them (morphological closing inside the board bbox).
|
|
13
|
+
* 2. Soft drop shadows darker than `tolerance` are not reached by the fill and leave a
|
|
14
|
+
* grey fringe around the board. Use method "ml" or a tighter crop.
|
|
15
|
+
* 3. A light, near-neutral board (white or silver PCB) that touches a corner or an edge
|
|
16
|
+
* midpoint seeds the fill itself and gets keyed. Crop so a margin of background
|
|
17
|
+
* surrounds the board, or use method "ml".
|
|
18
|
+
*/
|
|
19
|
+
import sharp from "sharp";
|
|
20
|
+
export const DEFAULT_TOLERANCE = 28;
|
|
21
|
+
/** Decode any sharp-supported image to straight RGBA, applying the EXIF orientation. */
|
|
22
|
+
export async function decodeRgba(data) {
|
|
23
|
+
const { data: buf, info } = await sharp(data)
|
|
24
|
+
.autoOrient()
|
|
25
|
+
.ensureAlpha()
|
|
26
|
+
.raw()
|
|
27
|
+
.toBuffer({ resolveWithObject: true });
|
|
28
|
+
return {
|
|
29
|
+
data: new Uint8Array(buf.buffer, buf.byteOffset, buf.length),
|
|
30
|
+
width: info.width,
|
|
31
|
+
height: info.height,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** Encode an RGBA image as PNG (deterministic settings). */
|
|
35
|
+
export async function encodePng(img) {
|
|
36
|
+
const out = await sharp(img.data, {
|
|
37
|
+
raw: { width: img.width, height: img.height, channels: 4 },
|
|
38
|
+
})
|
|
39
|
+
.png({ compressionLevel: 9, palette: false })
|
|
40
|
+
.toBuffer();
|
|
41
|
+
return new Uint8Array(out.buffer, out.byteOffset, out.length);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A seed may start a fill only if it looks like a listing background: light and nearly
|
|
45
|
+
* neutral, or already transparent. A board that touches an edge (tight crop) therefore
|
|
46
|
+
* does not get keyed from that edge.
|
|
47
|
+
*/
|
|
48
|
+
function isBackgroundSeed(d, i, tolerance) {
|
|
49
|
+
const r = d[i];
|
|
50
|
+
const g = d[i + 1];
|
|
51
|
+
const b = d[i + 2];
|
|
52
|
+
if (d[i + 3] < 16)
|
|
53
|
+
return true;
|
|
54
|
+
const lo = Math.min(r, g, b);
|
|
55
|
+
const hi = Math.max(r, g, b);
|
|
56
|
+
return lo >= 160 && hi - lo <= Math.max(2 * tolerance, 8);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Background mask: 1 = background (reached by a fill from a corner/edge seed), 0 = kept.
|
|
60
|
+
* Iterative stack fill with 4-connectivity; each pixel is pushed at most once.
|
|
61
|
+
*/
|
|
62
|
+
export function backgroundMask(img, tolerance = DEFAULT_TOLERANCE) {
|
|
63
|
+
const { data: d, width: w, height: h } = img;
|
|
64
|
+
const n = w * h;
|
|
65
|
+
const mask = new Uint8Array(n);
|
|
66
|
+
if (n === 0)
|
|
67
|
+
return mask;
|
|
68
|
+
const stack = new Int32Array(n);
|
|
69
|
+
const mx = Math.floor((w - 1) / 2);
|
|
70
|
+
const my = Math.floor((h - 1) / 2);
|
|
71
|
+
const seeds = [
|
|
72
|
+
[0, 0],
|
|
73
|
+
[w - 1, 0],
|
|
74
|
+
[0, h - 1],
|
|
75
|
+
[w - 1, h - 1],
|
|
76
|
+
[mx, 0],
|
|
77
|
+
[mx, h - 1],
|
|
78
|
+
[0, my],
|
|
79
|
+
[w - 1, my],
|
|
80
|
+
];
|
|
81
|
+
for (const [sx, sy] of seeds) {
|
|
82
|
+
const s = sy * w + sx;
|
|
83
|
+
if (mask[s] === 1 || !isBackgroundSeed(d, s * 4, tolerance))
|
|
84
|
+
continue;
|
|
85
|
+
const r0 = d[s * 4];
|
|
86
|
+
const g0 = d[s * 4 + 1];
|
|
87
|
+
const b0 = d[s * 4 + 2];
|
|
88
|
+
const matches = (p) => {
|
|
89
|
+
const i = p * 4;
|
|
90
|
+
if (d[i + 3] < 16)
|
|
91
|
+
return true;
|
|
92
|
+
return (Math.abs(d[i] - r0) <= tolerance &&
|
|
93
|
+
Math.abs(d[i + 1] - g0) <= tolerance &&
|
|
94
|
+
Math.abs(d[i + 2] - b0) <= tolerance);
|
|
95
|
+
};
|
|
96
|
+
let top = 0;
|
|
97
|
+
mask[s] = 1;
|
|
98
|
+
stack[top++] = s;
|
|
99
|
+
while (top > 0) {
|
|
100
|
+
const p = stack[--top];
|
|
101
|
+
const x = p % w;
|
|
102
|
+
if (x > 0 && mask[p - 1] === 0 && matches(p - 1)) {
|
|
103
|
+
mask[p - 1] = 1;
|
|
104
|
+
stack[top++] = p - 1;
|
|
105
|
+
}
|
|
106
|
+
if (x < w - 1 && mask[p + 1] === 0 && matches(p + 1)) {
|
|
107
|
+
mask[p + 1] = 1;
|
|
108
|
+
stack[top++] = p + 1;
|
|
109
|
+
}
|
|
110
|
+
if (p >= w && mask[p - w] === 0 && matches(p - w)) {
|
|
111
|
+
mask[p - w] = 1;
|
|
112
|
+
stack[top++] = p - w;
|
|
113
|
+
}
|
|
114
|
+
if (p + w < n && mask[p + w] === 0 && matches(p + w)) {
|
|
115
|
+
mask[p + w] = 1;
|
|
116
|
+
stack[top++] = p + w;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return mask;
|
|
121
|
+
}
|
|
122
|
+
/** Mark small isolated foreground islands (8-connected) as background, in place. */
|
|
123
|
+
function dropIslands(mask, w, h, minIslandPx) {
|
|
124
|
+
if (minIslandPx <= 1)
|
|
125
|
+
return;
|
|
126
|
+
const n = w * h;
|
|
127
|
+
const seen = new Uint8Array(n);
|
|
128
|
+
const stack = new Int32Array(n);
|
|
129
|
+
const members = [];
|
|
130
|
+
for (let start = 0; start < n; start++) {
|
|
131
|
+
if (mask[start] !== 0 || seen[start] === 1)
|
|
132
|
+
continue;
|
|
133
|
+
let top = 0;
|
|
134
|
+
let count = 0;
|
|
135
|
+
members.length = 0;
|
|
136
|
+
seen[start] = 1;
|
|
137
|
+
stack[top++] = start;
|
|
138
|
+
while (top > 0) {
|
|
139
|
+
const p = stack[--top];
|
|
140
|
+
count++;
|
|
141
|
+
if (count < minIslandPx)
|
|
142
|
+
members.push(p);
|
|
143
|
+
const x = p % w;
|
|
144
|
+
const y = (p - x) / w;
|
|
145
|
+
for (let dy = -1; dy <= 1; dy++) {
|
|
146
|
+
const ny = y + dy;
|
|
147
|
+
if (ny < 0 || ny >= h)
|
|
148
|
+
continue;
|
|
149
|
+
for (let dx = -1; dx <= 1; dx++) {
|
|
150
|
+
const nx = x + dx;
|
|
151
|
+
if (nx < 0 || nx >= w)
|
|
152
|
+
continue;
|
|
153
|
+
const q = ny * w + nx;
|
|
154
|
+
if (mask[q] === 0 && seen[q] === 0) {
|
|
155
|
+
seen[q] = 1;
|
|
156
|
+
stack[top++] = q;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (count < minIslandPx)
|
|
162
|
+
for (const p of members)
|
|
163
|
+
mask[p] = 1;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/** Bounding box of the pixels where `keep(p)` is true; w = h = 0 when there are none. */
|
|
167
|
+
export function maskBbox(w, h, keep) {
|
|
168
|
+
let x0 = w;
|
|
169
|
+
let y0 = h;
|
|
170
|
+
let x1 = -1;
|
|
171
|
+
let y1 = -1;
|
|
172
|
+
for (let y = 0; y < h; y++) {
|
|
173
|
+
const row = y * w;
|
|
174
|
+
for (let x = 0; x < w; x++) {
|
|
175
|
+
if (!keep(row + x))
|
|
176
|
+
continue;
|
|
177
|
+
if (x < x0)
|
|
178
|
+
x0 = x;
|
|
179
|
+
if (x > x1)
|
|
180
|
+
x1 = x;
|
|
181
|
+
if (y < y0)
|
|
182
|
+
y0 = y;
|
|
183
|
+
if (y > y1)
|
|
184
|
+
y1 = y;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (x1 < 0)
|
|
188
|
+
return { x: 0, y: 0, w: 0, h: 0 };
|
|
189
|
+
return { x: x0, y: y0, w: x1 - x0 + 1, h: y1 - y0 + 1 };
|
|
190
|
+
}
|
|
191
|
+
/** Bounding box of pixels with alpha >= 128. */
|
|
192
|
+
export function alphaBbox(img) {
|
|
193
|
+
return maskBbox(img.width, img.height, (p) => img.data[p * 4 + 3] >= 128);
|
|
194
|
+
}
|
|
195
|
+
/** Fill + island removal; returns the final background mask. */
|
|
196
|
+
function computeMask(img, opts) {
|
|
197
|
+
const tolerance = clamp(opts.tolerance ?? DEFAULT_TOLERANCE, 0, 255);
|
|
198
|
+
const mask = backgroundMask(img, tolerance);
|
|
199
|
+
const minIsland = opts.minIslandPx ?? Math.max(16, Math.floor((img.width * img.height) / 10000));
|
|
200
|
+
dropIslands(mask, img.width, img.height, minIsland);
|
|
201
|
+
if (opts.fillNotches)
|
|
202
|
+
fillNotches(mask, img);
|
|
203
|
+
return mask;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* One 1-D pass of a binary square-element dilation ("max") or erosion ("min") over the
|
|
207
|
+
* region [rx0, rx0 + rw) x [ry0, ry0 + rh) of a w x h image. Positions outside the image
|
|
208
|
+
* count as `edge` (1 for erosion so the image border does not erode); positions inside
|
|
209
|
+
* the image but outside the region count as 0 (nothing there is foreground).
|
|
210
|
+
*/
|
|
211
|
+
function morphPass(src, region, imgW, imgH, r, horizontal, mode) {
|
|
212
|
+
const { x: rx0, y: ry0, w: rw, h: rh } = region;
|
|
213
|
+
const out = new Uint8Array(rw * rh);
|
|
214
|
+
const lines = horizontal ? rh : rw;
|
|
215
|
+
const len = horizontal ? rw : rh;
|
|
216
|
+
const origin = horizontal ? rx0 : ry0;
|
|
217
|
+
const extent = horizontal ? imgW : imgH;
|
|
218
|
+
const win = 2 * r + 1;
|
|
219
|
+
for (let l = 0; l < lines; l++) {
|
|
220
|
+
const at = (i) => {
|
|
221
|
+
if (i >= 0 && i < len)
|
|
222
|
+
return src[horizontal ? l * rw + i : i * rw + l];
|
|
223
|
+
const g = origin + i;
|
|
224
|
+
return mode === "min" && (g < 0 || g >= extent) ? 1 : 0;
|
|
225
|
+
};
|
|
226
|
+
let sum = 0;
|
|
227
|
+
for (let i = -r; i <= r; i++)
|
|
228
|
+
sum += at(i);
|
|
229
|
+
for (let i = 0; i < len; i++) {
|
|
230
|
+
const v = mode === "max" ? (sum > 0 ? 1 : 0) : sum === win ? 1 : 0;
|
|
231
|
+
out[horizontal ? l * rw + i : i * rw + l] = v;
|
|
232
|
+
sum += at(i + r + 1) - at(i - r);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return out;
|
|
236
|
+
}
|
|
237
|
+
/** Morphological closing of the opaque mask, applied inside the board bbox only (in place). */
|
|
238
|
+
function fillNotches(mask, img) {
|
|
239
|
+
const { width: w, height: h } = img;
|
|
240
|
+
const box = boardBox(img, mask);
|
|
241
|
+
if (box.w === 0 || box.h === 0)
|
|
242
|
+
return;
|
|
243
|
+
const r = clamp(Math.round(0.025 * Math.min(box.w, box.h)), 2, 24);
|
|
244
|
+
const x0 = Math.max(0, box.x - r);
|
|
245
|
+
const y0 = Math.max(0, box.y - r);
|
|
246
|
+
const region = { x: x0, y: y0, w: Math.min(w, box.x + box.w + r) - x0, h: 0 };
|
|
247
|
+
region.h = Math.min(h, box.y + box.h + r) - y0;
|
|
248
|
+
const fg = new Uint8Array(region.w * region.h);
|
|
249
|
+
for (let y = 0; y < region.h; y++) {
|
|
250
|
+
for (let x = 0; x < region.w; x++) {
|
|
251
|
+
fg[y * region.w + x] = mask[(y0 + y) * w + x0 + x] === 0 ? 1 : 0;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
let m = morphPass(fg, region, w, h, r, true, "max");
|
|
255
|
+
m = morphPass(m, region, w, h, r, false, "max");
|
|
256
|
+
m = morphPass(m, region, w, h, r, true, "min");
|
|
257
|
+
m = morphPass(m, region, w, h, r, false, "min");
|
|
258
|
+
for (let y = box.y; y < box.y + box.h; y++) {
|
|
259
|
+
for (let x = box.x; x < box.x + box.w; x++) {
|
|
260
|
+
if (m[(y - y0) * region.w + x - x0] === 1)
|
|
261
|
+
mask[y * w + x] = 0;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
function boardBox(img, mask) {
|
|
266
|
+
return maskBbox(img.width, img.height, (p) => mask[p] === 0 && img.data[p * 4 + 3] >= 16);
|
|
267
|
+
}
|
|
268
|
+
/** Key the background of a decoded image. Returns a new RGBA image and the board bbox. */
|
|
269
|
+
export async function keyRgba(img, opts = {}) {
|
|
270
|
+
const { width: w, height: h } = img;
|
|
271
|
+
const n = w * h;
|
|
272
|
+
const mask = computeMask(img, opts);
|
|
273
|
+
const out = new Uint8Array(img.data);
|
|
274
|
+
for (let p = 0; p < n; p++)
|
|
275
|
+
if (mask[p] === 1)
|
|
276
|
+
out[p * 4 + 3] = 0;
|
|
277
|
+
const feather = opts.feather ?? 0;
|
|
278
|
+
if (feather > 0 && n > 0) {
|
|
279
|
+
const alpha = new Uint8Array(n);
|
|
280
|
+
for (let p = 0; p < n; p++)
|
|
281
|
+
alpha[p] = mask[p] === 1 ? 0 : 255;
|
|
282
|
+
const blurred = await sharp(alpha, { raw: { width: w, height: h, channels: 1 } })
|
|
283
|
+
.blur(Math.max(0.3, feather))
|
|
284
|
+
.extractChannel(0) // sharp promotes 1-channel raw input to sRGB
|
|
285
|
+
.raw()
|
|
286
|
+
.toBuffer();
|
|
287
|
+
// Only erode: the background stays transparent and the board edge fades out.
|
|
288
|
+
for (let p = 0; p < n; p++) {
|
|
289
|
+
const b = blurred[p];
|
|
290
|
+
if (b < out[p * 4 + 3])
|
|
291
|
+
out[p * 4 + 3] = b;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return { image: { data: out, width: w, height: h }, bbox: boardBox(img, mask) };
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Make the near-white listing background transparent. `data` is any image sharp can
|
|
298
|
+
* decode (PNG, JPEG, WebP, ...). Throws if it cannot be decoded.
|
|
299
|
+
*/
|
|
300
|
+
export async function keyBackground(data, opts = {}) {
|
|
301
|
+
const img = await decodeRgba(data);
|
|
302
|
+
const { image, bbox } = await keyRgba(img, opts);
|
|
303
|
+
return { png: await encodePng(image), bbox };
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Detect the board's bounding box (same fill as keyBackground, no output image). The UI
|
|
307
|
+
* uses this for calibration together with bbox.ts. Throws if `data` cannot be decoded.
|
|
308
|
+
*/
|
|
309
|
+
export async function detectBoardBbox(data, opts = {}) {
|
|
310
|
+
const img = await decodeRgba(data);
|
|
311
|
+
const box = boardBox(img, computeMask(img, opts));
|
|
312
|
+
return { ...box, widthPx: img.width, heightPx: img.height };
|
|
313
|
+
}
|
|
314
|
+
export function clamp(v, lo, hi) {
|
|
315
|
+
return Math.min(hi, Math.max(lo, v));
|
|
316
|
+
}
|
|
317
|
+
//# sourceMappingURL=key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key.js","sourceRoot":"","sources":["../../../src/core/image/key.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { PreparedPhoto } from "../compile/types.js";
|
|
2
|
+
import type { Photo } from "../schema/part.js";
|
|
3
|
+
import { type KeyOptions, type PixelBox } from "./key.js";
|
|
4
|
+
import { type RemoverOptions } from "./remover.js";
|
|
5
|
+
export type RemovalMethod = "key" | "ml" | "none";
|
|
6
|
+
export interface PrepareOptions {
|
|
7
|
+
/** Cap on the output width in pixels (keeps the .fzpz small). Default 1600. */
|
|
8
|
+
maxWidthPx?: number;
|
|
9
|
+
/** Background removal method. Default "key" (sharp flood fill); "ml" needs an optional AGPL package. */
|
|
10
|
+
method?: RemovalMethod;
|
|
11
|
+
/** Options for the "key" method (tolerance, feather, minIslandPx, fillNotches). */
|
|
12
|
+
key?: KeyOptions;
|
|
13
|
+
/** Options for the "ml" method (tests inject a fake loader). */
|
|
14
|
+
remover?: RemoverOptions;
|
|
15
|
+
}
|
|
16
|
+
/** The part of the spec preparePhoto reads (a full PartSpec satisfies it). */
|
|
17
|
+
export interface PhotoSpec {
|
|
18
|
+
artwork: {
|
|
19
|
+
photo?: Partial<Photo> | undefined;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export declare const DEFAULT_MAX_WIDTH_PX = 1600;
|
|
23
|
+
/** Clamp a [x, y, w, h] crop to the image; undefined when nothing is left. */
|
|
24
|
+
export declare function clampCrop(crop: readonly [number, number, number, number], width: number, height: number): PixelBox | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Prepare the photo for embedding. Returns undefined (never throws) when the image
|
|
27
|
+
* cannot be decoded. Method "ml" may reject: with the install hint when the optional
|
|
28
|
+
* package is missing, or with the package's own error when loading or running it fails,
|
|
29
|
+
* so the caller can surface it.
|
|
30
|
+
*/
|
|
31
|
+
export declare function preparePhoto(data: Uint8Array, spec: PhotoSpec, opts?: PrepareOptions): Promise<PreparedPhoto | undefined>;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn a photo asset into the PreparedPhoto the breadboard renderer embeds
|
|
3
|
+
* (spec decision D19). Pipeline: decode (EXIF orientation applied) -> crop -> rotate ->
|
|
4
|
+
* background removal + trim to the board -> downscale -> PNG.
|
|
5
|
+
*
|
|
6
|
+
* The crop is in the pixels of the EXIF-oriented image (as a viewer shows it), applied
|
|
7
|
+
* before rotation. `rotate` turns the cropped image CLOCKWISE by 90/180/270 degrees
|
|
8
|
+
* (sharp's convention), so rotate: 90 moves the top-left corner to the top-right.
|
|
9
|
+
*
|
|
10
|
+
* The renderer stretches the PNG over the outline rect (preserveAspectRatio="none"), so
|
|
11
|
+
* the trimmed board must map 1:1 onto the outline; we only cap the width.
|
|
12
|
+
*/
|
|
13
|
+
import sharp from "sharp";
|
|
14
|
+
import { alphaBbox, clamp, decodeRgba, encodePng, keyRgba, } from "./key.js";
|
|
15
|
+
import { removeBackgroundMl } from "./remover.js";
|
|
16
|
+
export const DEFAULT_MAX_WIDTH_PX = 1600;
|
|
17
|
+
/** Clamp a [x, y, w, h] crop to the image; undefined when nothing is left. */
|
|
18
|
+
export function clampCrop(crop, width, height) {
|
|
19
|
+
const x0 = clamp(Math.round(crop[0]), 0, width);
|
|
20
|
+
const y0 = clamp(Math.round(crop[1]), 0, height);
|
|
21
|
+
const x1 = clamp(Math.round(crop[0] + crop[2]), 0, width);
|
|
22
|
+
const y1 = clamp(Math.round(crop[1] + crop[3]), 0, height);
|
|
23
|
+
if (x1 - x0 <= 0 || y1 - y0 <= 0)
|
|
24
|
+
return undefined;
|
|
25
|
+
return { x: x0, y: y0, w: x1 - x0, h: y1 - y0 };
|
|
26
|
+
}
|
|
27
|
+
function raw(img) {
|
|
28
|
+
return sharp(img.data, { raw: { width: img.width, height: img.height, channels: 4 } });
|
|
29
|
+
}
|
|
30
|
+
async function toRgba(s) {
|
|
31
|
+
const { data, info } = await s.raw().toBuffer({ resolveWithObject: true });
|
|
32
|
+
return {
|
|
33
|
+
data: new Uint8Array(data.buffer, data.byteOffset, data.length),
|
|
34
|
+
width: info.width,
|
|
35
|
+
height: info.height,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
async function extract(img, box) {
|
|
39
|
+
if (box.x === 0 && box.y === 0 && box.w === img.width && box.h === img.height)
|
|
40
|
+
return img;
|
|
41
|
+
return toRgba(raw(img).extract({ left: box.x, top: box.y, width: box.w, height: box.h }));
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Prepare the photo for embedding. Returns undefined (never throws) when the image
|
|
45
|
+
* cannot be decoded. Method "ml" may reject: with the install hint when the optional
|
|
46
|
+
* package is missing, or with the package's own error when loading or running it fails,
|
|
47
|
+
* so the caller can surface it.
|
|
48
|
+
*/
|
|
49
|
+
export async function preparePhoto(data, spec, opts = {}) {
|
|
50
|
+
const photo = spec.artwork.photo ?? {};
|
|
51
|
+
const method = opts.method ?? "key";
|
|
52
|
+
const maxWidth = Math.max(1, Math.floor(opts.maxWidthPx ?? DEFAULT_MAX_WIDTH_PX));
|
|
53
|
+
let img;
|
|
54
|
+
try {
|
|
55
|
+
img = await decodeRgba(data);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
if (img.width === 0 || img.height === 0)
|
|
61
|
+
return undefined;
|
|
62
|
+
if (photo.crop) {
|
|
63
|
+
const box = clampCrop(photo.crop, img.width, img.height);
|
|
64
|
+
if (box)
|
|
65
|
+
img = await extract(img, box);
|
|
66
|
+
}
|
|
67
|
+
const rotate = photo.rotate ?? 0;
|
|
68
|
+
if (rotate !== 0)
|
|
69
|
+
img = await toRgba(raw(img).rotate(rotate));
|
|
70
|
+
if (photo.removeBackground ?? true) {
|
|
71
|
+
let trim;
|
|
72
|
+
if (method === "key") {
|
|
73
|
+
const keyed = await keyRgba(img, opts.key);
|
|
74
|
+
img = keyed.image;
|
|
75
|
+
trim = keyed.bbox;
|
|
76
|
+
}
|
|
77
|
+
else if (method === "ml") {
|
|
78
|
+
const out = await removeBackgroundMl(await encodePng(img), opts.remover);
|
|
79
|
+
img = await decodeRgba(out);
|
|
80
|
+
trim = alphaBbox(img);
|
|
81
|
+
}
|
|
82
|
+
if (trim && trim.w > 0 && trim.h > 0)
|
|
83
|
+
img = await extract(img, trim);
|
|
84
|
+
}
|
|
85
|
+
let s = raw(img);
|
|
86
|
+
if (img.width > maxWidth)
|
|
87
|
+
s = s.resize({ width: maxWidth, kernel: "lanczos3" });
|
|
88
|
+
const { data: png, info } = await s
|
|
89
|
+
.png({ compressionLevel: 9, palette: false })
|
|
90
|
+
.toBuffer({ resolveWithObject: true });
|
|
91
|
+
return {
|
|
92
|
+
png: new Uint8Array(png.buffer, png.byteOffset, png.length),
|
|
93
|
+
widthPx: info.width,
|
|
94
|
+
heightPx: info.height,
|
|
95
|
+
opacity: photo.opacity ?? 1,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=prepare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prepare.js","sourceRoot":"","sources":["../../../src/core/image/prepare.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAqB,MAAM,OAAO,CAAC;AAG1C,OAAO,EACL,SAAS,EACT,KAAK,EACL,UAAU,EACV,SAAS,EACT,OAAO,GAIR,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,kBAAkB,EAAuB,MAAM,cAAc,CAAC;AAoBvE,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAC;AAEzC,8EAA8E;AAC9E,MAAM,UAAU,SAAS,CACvB,IAA+C,EAC/C,KAAa,EACb,MAAc;IAEd,MAAM,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;IAChD,MAAM,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;IACjD,MAAM,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;IAC1D,MAAM,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;IAC3D,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACnD,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC;AAClD,CAAC;AAED,SAAS,GAAG,CAAC,GAAc;IACzB,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AACzF,CAAC;AAED,KAAK,UAAU,MAAM,CAAC,CAAQ;IAC5B,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3E,OAAO;QACL,IAAI,EAAE,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC;QAC/D,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,MAAM,EAAE,IAAI,CAAC,MAAM;KACpB,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,OAAO,CAAC,GAAc,EAAE,GAAa;IAClD,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC,MAAM;QAAE,OAAO,GAAG,CAAC;IAC1F,OAAO,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC5F,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,IAAgB,EAChB,IAAe,EACf,OAAuB,EAAE;IAEzB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;IACvC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC;IACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,IAAI,oBAAoB,CAAC,CAAC,CAAC;IAElF,IAAI,GAAc,CAAC;IACnB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,GAAG,CAAC,KAAK,KAAK,CAAC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAE1D,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;QACzD,IAAI,GAAG;YAAE,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACzC,CAAC;IACD,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;IACjC,IAAI,MAAM,KAAK,CAAC;QAAE,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAE9D,IAAI,KAAK,CAAC,gBAAgB,IAAI,IAAI,EAAE,CAAC;QACnC,IAAI,IAA0B,CAAC;QAC/B,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YACrB,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;YAC3C,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC;YAClB,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QACpB,CAAC;aAAM,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,MAAM,kBAAkB,CAAC,MAAM,SAAS,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YACzE,GAAG,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC;YAC5B,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;QACxB,CAAC;QACD,IAAI,IAAI,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC;YAAE,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACvE,CAAC;IAED,IAAI,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IACjB,IAAI,GAAG,CAAC,KAAK,GAAG,QAAQ;QAAE,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;IAChF,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC;SAChC,GAAG,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;SAC5C,QAAQ,CAAC,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,OAAO;QACL,GAAG,EAAE,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC;QAC3D,OAAO,EAAE,IAAI,CAAC,KAAK;QACnB,QAAQ,EAAE,IAAI,CAAC,MAAM;QACrB,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,CAAC;KAC5B,CAAC;AACJ,CAAC"}
|