@jjlmoya/utils-forensic-science 1.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/package.json +69 -0
- package/scripts/crystal-lattice-catalog-translations.mjs +459 -0
- package/scripts/postinstall.mjs +27 -0
- package/scripts/sync-crystal-lattice-i18n.mjs +50 -0
- package/src/category/i18n/de.ts +55 -0
- package/src/category/i18n/en.ts +55 -0
- package/src/category/i18n/es.ts +55 -0
- package/src/category/i18n/fr.ts +55 -0
- package/src/category/i18n/id.ts +55 -0
- package/src/category/i18n/it.ts +55 -0
- package/src/category/i18n/ja.ts +56 -0
- package/src/category/i18n/ko.ts +56 -0
- package/src/category/i18n/nl.ts +55 -0
- package/src/category/i18n/pl.ts +55 -0
- package/src/category/i18n/pt.ts +55 -0
- package/src/category/i18n/ru.ts +55 -0
- package/src/category/i18n/sv.ts +55 -0
- package/src/category/i18n/tr.ts +55 -0
- package/src/category/i18n/zh.ts +56 -0
- package/src/category/index.ts +48 -0
- package/src/category/seo.astro +15 -0
- package/src/components/PreviewNavSidebar.astro +116 -0
- package/src/components/PreviewToolbar.astro +143 -0
- package/src/data.ts +10 -0
- package/src/entries.ts +48 -0
- package/src/env.d.ts +5 -0
- package/src/index.ts +29 -0
- package/src/layouts/PreviewLayout.astro +117 -0
- package/src/pages/[locale]/[slug].astro +162 -0
- package/src/pages/[locale].astro +261 -0
- package/src/pages/index.astro +3 -0
- package/src/tests/diacritics_density.test.ts +140 -0
- package/src/tests/faq_count.test.ts +19 -0
- package/src/tests/i18n_coverage.test.ts +36 -0
- package/src/tests/inverted_punctuation.test.ts +84 -0
- package/src/tests/locale_completeness.test.ts +24 -0
- package/src/tests/mocks/astro_mock.js +2 -0
- package/src/tests/no_en_dash.test.ts +70 -0
- package/src/tests/no_h1_in_components.test.ts +48 -0
- package/src/tests/schemas_fulfillment.test.ts +23 -0
- package/src/tests/script_density.test.ts +94 -0
- package/src/tests/seo_length.test.ts +46 -0
- package/src/tests/shared-test-helpers.ts +56 -0
- package/src/tests/slug_language_code_format.test.ts +23 -0
- package/src/tests/slug_uniqueness.test.ts +81 -0
- package/src/tests/title_quality.test.ts +55 -0
- package/src/tests/tool_exports.test.ts +34 -0
- package/src/tests/tool_validation.test.ts +16 -0
- package/src/tests/widmark.test.ts +28 -0
- package/src/tool/forensic-age-estimator/bibliography.astro +14 -0
- package/src/tool/forensic-age-estimator/bibliography.ts +16 -0
- package/src/tool/forensic-age-estimator/component.astro +321 -0
- package/src/tool/forensic-age-estimator/dental-skeletal-third-molar-age-estimator.css +634 -0
- package/src/tool/forensic-age-estimator/entry.ts +32 -0
- package/src/tool/forensic-age-estimator/i18n/de.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/en.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/es.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/fr.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/id.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/it.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/ja.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/ko.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/nl.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/pl.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/pt.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/ru.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/sv.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/tr.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/zh.ts +246 -0
- package/src/tool/forensic-age-estimator/index.ts +11 -0
- package/src/tool/forensic-age-estimator/logic.ts +81 -0
- package/src/tool/forensic-age-estimator/seo.astro +15 -0
- package/src/tool/forensic-blood-test-simulator/bibliography.astro +6 -0
- package/src/tool/forensic-blood-test-simulator/bibliography.ts +12 -0
- package/src/tool/forensic-blood-test-simulator/component.astro +168 -0
- package/src/tool/forensic-blood-test-simulator/dom-utils.ts +40 -0
- package/src/tool/forensic-blood-test-simulator/entry.ts +32 -0
- package/src/tool/forensic-blood-test-simulator/forensic-presumptive-blood-testing-luminol-kastle-meyer-simulator.css +532 -0
- package/src/tool/forensic-blood-test-simulator/helpers.ts +16 -0
- package/src/tool/forensic-blood-test-simulator/i18n/de.ts +185 -0
- package/src/tool/forensic-blood-test-simulator/i18n/en.ts +185 -0
- package/src/tool/forensic-blood-test-simulator/i18n/es.ts +185 -0
- package/src/tool/forensic-blood-test-simulator/i18n/fr.ts +220 -0
- package/src/tool/forensic-blood-test-simulator/i18n/id.ts +220 -0
- package/src/tool/forensic-blood-test-simulator/i18n/it.ts +220 -0
- package/src/tool/forensic-blood-test-simulator/i18n/ja.ts +220 -0
- package/src/tool/forensic-blood-test-simulator/i18n/ko.ts +220 -0
- package/src/tool/forensic-blood-test-simulator/i18n/nl.ts +220 -0
- package/src/tool/forensic-blood-test-simulator/i18n/pl.ts +220 -0
- package/src/tool/forensic-blood-test-simulator/i18n/pt.ts +220 -0
- package/src/tool/forensic-blood-test-simulator/i18n/ru.ts +220 -0
- package/src/tool/forensic-blood-test-simulator/i18n/sv.ts +220 -0
- package/src/tool/forensic-blood-test-simulator/i18n/tr.ts +220 -0
- package/src/tool/forensic-blood-test-simulator/i18n/zh.ts +220 -0
- package/src/tool/forensic-blood-test-simulator/index.ts +11 -0
- package/src/tool/forensic-blood-test-simulator/logic.ts +174 -0
- package/src/tool/forensic-blood-test-simulator/script.ts +192 -0
- package/src/tool/forensic-blood-test-simulator/seo.astro +15 -0
- package/src/tool/forensic-fiber-comparison-microscope/bibliography.astro +6 -0
- package/src/tool/forensic-fiber-comparison-microscope/bibliography.ts +16 -0
- package/src/tool/forensic-fiber-comparison-microscope/component.astro +146 -0
- package/src/tool/forensic-fiber-comparison-microscope/entry.ts +32 -0
- package/src/tool/forensic-fiber-comparison-microscope/forensic-fiber-comparison-microscope.css +629 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/de.ts +250 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/en.ts +246 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/es.ts +246 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/fr.ts +246 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/id.ts +250 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/it.ts +246 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/ja.ts +250 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/ko.ts +246 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/nl.ts +246 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/pl.ts +250 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/pt.ts +250 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/ru.ts +250 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/sv.ts +250 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/tr.ts +246 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/zh.ts +250 -0
- package/src/tool/forensic-fiber-comparison-microscope/index.ts +11 -0
- package/src/tool/forensic-fiber-comparison-microscope/logic.ts +244 -0
- package/src/tool/forensic-fiber-comparison-microscope/render.ts +265 -0
- package/src/tool/forensic-fiber-comparison-microscope/seo.astro +15 -0
- package/src/tool/forensic-fiber-comparison-microscope/view.ts +267 -0
- package/src/tool/forensic-glass-becke-line-simulator/bibliography.astro +6 -0
- package/src/tool/forensic-glass-becke-line-simulator/bibliography.ts +16 -0
- package/src/tool/forensic-glass-becke-line-simulator/component.astro +81 -0
- package/src/tool/forensic-glass-becke-line-simulator/entry.ts +32 -0
- package/src/tool/forensic-glass-becke-line-simulator/forensic-glass-becke-line-simulator.css +392 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/de.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/en.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/es.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/fr.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/id.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/it.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/ja.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/ko.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/nl.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/pl.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/pt.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/ru.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/sv.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/tr.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/zh.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/index.ts +11 -0
- package/src/tool/forensic-glass-becke-line-simulator/logic.ts +100 -0
- package/src/tool/forensic-glass-becke-line-simulator/seo.astro +15 -0
- package/src/tool/forensic-glass-becke-line-simulator/view.ts +281 -0
- package/src/tool/forensic-image-authenticity-analyzer/bibliography.astro +9 -0
- package/src/tool/forensic-image-authenticity-analyzer/bibliography.ts +7 -0
- package/src/tool/forensic-image-authenticity-analyzer/component.astro +250 -0
- package/src/tool/forensic-image-authenticity-analyzer/entry.ts +29 -0
- package/src/tool/forensic-image-authenticity-analyzer/forensic-image-metadata-authenticity-analyzer.css +679 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/de.ts +105 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/en.ts +105 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/es.ts +105 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/fr.ts +105 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/id.ts +76 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/it.ts +105 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/ja.ts +72 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/ko.ts +72 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/nl.ts +75 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/pl.ts +75 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/pt.ts +105 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/ru.ts +75 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/sv.ts +76 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/tr.ts +76 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/zh.ts +71 -0
- package/src/tool/forensic-image-authenticity-analyzer/index.ts +11 -0
- package/src/tool/forensic-image-authenticity-analyzer/logic.ts +283 -0
- package/src/tool/forensic-image-authenticity-analyzer/seo.astro +10 -0
- package/src/tool/forensic-microcrystal-drug-simulator/bibliography.astro +6 -0
- package/src/tool/forensic-microcrystal-drug-simulator/bibliography.ts +12 -0
- package/src/tool/forensic-microcrystal-drug-simulator/component.astro +240 -0
- package/src/tool/forensic-microcrystal-drug-simulator/entry.ts +32 -0
- package/src/tool/forensic-microcrystal-drug-simulator/forensic-microcrystal-drug-simulator.css +430 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/de.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/en.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/es.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/fr.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/id.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/it.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/ja.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/ko.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/nl.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/pl.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/pt.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/ru.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/sv.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/tr.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/zh.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/index.ts +11 -0
- package/src/tool/forensic-microcrystal-drug-simulator/logic.ts +189 -0
- package/src/tool/forensic-microcrystal-drug-simulator/seo.astro +15 -0
- package/src/tool/forensic-sex-determinator/bibliography.astro +14 -0
- package/src/tool/forensic-sex-determinator/bibliography.ts +12 -0
- package/src/tool/forensic-sex-determinator/component.astro +463 -0
- package/src/tool/forensic-sex-determinator/entry.ts +32 -0
- package/src/tool/forensic-sex-determinator/forensic-sex-determinator.css +413 -0
- package/src/tool/forensic-sex-determinator/i18n/de.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/en.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/es.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/fr.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/id.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/it.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/ja.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/ko.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/nl.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/pl.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/pt.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/ru.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/sv.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/tr.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/zh.ts +211 -0
- package/src/tool/forensic-sex-determinator/index.ts +11 -0
- package/src/tool/forensic-sex-determinator/logic.ts +89 -0
- package/src/tool/forensic-sex-determinator/seo.astro +15 -0
- package/src/tool/forensic-stature-estimator/bibliography.astro +14 -0
- package/src/tool/forensic-stature-estimator/bibliography.ts +12 -0
- package/src/tool/forensic-stature-estimator/component.astro +49 -0
- package/src/tool/forensic-stature-estimator/components/EstimationPanel.astro +65 -0
- package/src/tool/forensic-stature-estimator/components/OsteometricBoard.astro +39 -0
- package/src/tool/forensic-stature-estimator/components/OsteometricSelector.astro +109 -0
- package/src/tool/forensic-stature-estimator/dom-utils.ts +71 -0
- package/src/tool/forensic-stature-estimator/entry.ts +32 -0
- package/src/tool/forensic-stature-estimator/forensic-stature-estimator.css +689 -0
- package/src/tool/forensic-stature-estimator/helpers.ts +51 -0
- package/src/tool/forensic-stature-estimator/i18n/de.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/en.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/es.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/fr.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/id.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/it.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/ja.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/ko.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/nl.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/pl.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/pt.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/ru.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/sv.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/tr.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/zh.ts +196 -0
- package/src/tool/forensic-stature-estimator/index.ts +11 -0
- package/src/tool/forensic-stature-estimator/logic.ts +119 -0
- package/src/tool/forensic-stature-estimator/script.ts +288 -0
- package/src/tool/forensic-stature-estimator/seo.astro +15 -0
- package/src/tool/forensic-tlc-ink-simulator/bibliography.astro +6 -0
- package/src/tool/forensic-tlc-ink-simulator/bibliography.ts +16 -0
- package/src/tool/forensic-tlc-ink-simulator/component.astro +245 -0
- package/src/tool/forensic-tlc-ink-simulator/entry.ts +32 -0
- package/src/tool/forensic-tlc-ink-simulator/forensic-tlc-ink-simulator.css +462 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/de.ts +243 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/en.ts +243 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/es.ts +243 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/fr.ts +243 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/id.ts +243 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/it.ts +243 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/ja.ts +235 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/ko.ts +235 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/nl.ts +243 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/pl.ts +243 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/pt.ts +243 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/ru.ts +243 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/sv.ts +243 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/tr.ts +243 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/zh.ts +235 -0
- package/src/tool/forensic-tlc-ink-simulator/index.ts +11 -0
- package/src/tool/forensic-tlc-ink-simulator/logic.ts +152 -0
- package/src/tool/forensic-tlc-ink-simulator/seo.astro +15 -0
- package/src/tool/gsr-dispersion-calculator/bibliography.astro +6 -0
- package/src/tool/gsr-dispersion-calculator/bibliography.ts +16 -0
- package/src/tool/gsr-dispersion-calculator/component.astro +294 -0
- package/src/tool/gsr-dispersion-calculator/entry.ts +32 -0
- package/src/tool/gsr-dispersion-calculator/gsr-dispersion-calculator.css +305 -0
- package/src/tool/gsr-dispersion-calculator/i18n/de.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/en.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/es.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/fr.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/id.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/it.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/ja.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/ko.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/nl.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/pl.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/pt.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/ru.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/sv.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/tr.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/zh.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/index.ts +11 -0
- package/src/tool/gsr-dispersion-calculator/logic.ts +148 -0
- package/src/tool/gsr-dispersion-calculator/seo.astro +15 -0
- package/src/tool/widmark-alcohol-simulator/bibliography.astro +14 -0
- package/src/tool/widmark-alcohol-simulator/bibliography.ts +12 -0
- package/src/tool/widmark-alcohol-simulator/component.astro +453 -0
- package/src/tool/widmark-alcohol-simulator/entry.ts +32 -0
- package/src/tool/widmark-alcohol-simulator/i18n/de.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/en.ts +206 -0
- package/src/tool/widmark-alcohol-simulator/i18n/es.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/fr.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/id.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/it.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/ja.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/ko.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/nl.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/pl.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/pt.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/ru.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/sv.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/tr.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/zh.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/index.ts +11 -0
- package/src/tool/widmark-alcohol-simulator/logic.ts +97 -0
- package/src/tool/widmark-alcohol-simulator/seo.astro +15 -0
- package/src/tool/widmark-alcohol-simulator/widmark-alcohol-simulator.css +386 -0
- package/src/tools.ts +27 -0
- package/src/types.ts +70 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { bibliography } from '../bibliography';
|
|
2
|
+
import type { ToolLocaleContent } from '../../../types';
|
|
3
|
+
|
|
4
|
+
const slug = 'gsr-dispersion-calculator';
|
|
5
|
+
const title = '射击残留物(GSR)扩散距离计算器';
|
|
6
|
+
const description = '根据射击距离、弹药类型、扩散分布、烟灰沉积和热效应,模拟在目标表面上射击残留物的分布图样,用于法医学教学。';
|
|
7
|
+
|
|
8
|
+
const howTo = [
|
|
9
|
+
{
|
|
10
|
+
name: '设置枪口到目标的距离',
|
|
11
|
+
text: '将距离控制杆从接触范围移动到三米,观察相同的射击如何从紧凑、有热变化的沉积物变为宽广、稀疏 pleasure 的残留物颗粒场。',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
name: '选择弹药配置文件',
|
|
15
|
+
text: '选择 9 mm 手枪、.38 左轮手枪或 12 号口径散弹枪配置文件。每个配置文件会改变模拟所用的颗粒计数、扩散系数和估计的防热效应范围。',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: '检查目标布和扩散圆锥体',
|
|
19
|
+
text: '使用目标布比较颗粒密度和放射状扩散,然后使用侧面圆锥体了解射击羽流在到达目标平面之前是如何物理膨胀的。',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: '解读距离类别',
|
|
23
|
+
text: '查看分类的距离类别和预期所见。结果描述了接触、近接触、近距离和远距离图样的教学意义,而不作为具体的案件鉴定结论。',
|
|
24
|
+
},
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const faq = [
|
|
28
|
+
{
|
|
29
|
+
key: 'faq-1',
|
|
30
|
+
question: '射击残留物的扩散能确定绝对准确的射击距离吗?',
|
|
31
|
+
answer: '不能。GSR 图样仅支持估算一个射击距离范围,而非一个精确的数值。真实的射击距离判定需要对特定枪支、弹药、目标材料、射击角度进行检验,并在控制条件下进行比较试射。',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
key: 'faq-2',
|
|
35
|
+
question: '为什么极近距离射击会显示更多的烟灰和烧灼痕迹?',
|
|
36
|
+
answer: '在极短距离内,高温气体、烟灰、未燃烧的火药颗粒以及底火中的金属颗粒在射击羽流膨胀和冷却之前就已撞击目标,导致发黑、烧焦、撕裂和枪口印痕。',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
key: 'faq-3',
|
|
40
|
+
question: '雷管底火 GSR 中通常包含哪些化学元素?',
|
|
41
|
+
answer: '传统的 GSR 分析专注于含有铅、钡和锑的颗粒,尽管现代的无铅弹药可能改变这种特征化学元素谱。SEM-EDS 分析通常结合颗粒的形态和化学成分进行综合评估。',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
key: 'faq-4',
|
|
45
|
+
question: '为什么模拟器使用高斯径向分布模型?',
|
|
46
|
+
answer: '高斯径向分布是一个很有用的教学近似模型,因为大多数颗粒都集中在撞击中心附近。在实际情况中,风向、射击角度、枪支设计或织物结构可能会引起非对称图样。',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
key: 'faq-5',
|
|
50
|
+
question: '这个计算器适用于出具法庭证据报告吗?',
|
|
51
|
+
answer: '不适用。这仅是一个教学可视化工具。正式的物证司法鉴定必须依赖经过验证的实验室规程、质量控制、实物比对图样以及有资质的鉴定人员的评估。',
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
export const content: ToolLocaleContent = {
|
|
56
|
+
slug,
|
|
57
|
+
title,
|
|
58
|
+
description,
|
|
59
|
+
ui: {
|
|
60
|
+
distance: '射击距离',
|
|
61
|
+
units: '单位系统',
|
|
62
|
+
metric: '公制',
|
|
63
|
+
imperial: '英制',
|
|
64
|
+
unitMeter: '米',
|
|
65
|
+
unitCentimeter: '厘米',
|
|
66
|
+
unitFoot: '英尺',
|
|
67
|
+
unitInch: '英寸',
|
|
68
|
+
ammo: '弹药配置文件',
|
|
69
|
+
ammoPistol9mm: '9 mm 手枪',
|
|
70
|
+
ammoRevolver38: '.38 左轮手枪',
|
|
71
|
+
ammoShotgun12: '12 号口径散弹枪',
|
|
72
|
+
target: '目标布',
|
|
73
|
+
cone: '残留物羽流',
|
|
74
|
+
canvasAria: '目标布上的射击残留物颗粒扩散图样',
|
|
75
|
+
rangeStripAria: '距离类别',
|
|
76
|
+
category: '距离解读',
|
|
77
|
+
categoryContactLabel: '接触射击 / 枪口贴片',
|
|
78
|
+
categoryNearContactLabel: '近接触射击 / 烧灼区',
|
|
79
|
+
categoryCloseRangeLabel: '近距离射击 / 火药纹身区',
|
|
80
|
+
categoryDistantLabel: '远距离或不确定距离',
|
|
81
|
+
density: '颗粒密度',
|
|
82
|
+
thermal: '热效应',
|
|
83
|
+
radius: '估计图样半径',
|
|
84
|
+
radiusSuffix: '半径',
|
|
85
|
+
expected: '预期所见',
|
|
86
|
+
expectedContact: '预期在目标区有高密度的中心沉积物、枪口印痕风险、烟灰发黑、撕裂和热变性。',
|
|
87
|
+
expectedNearContact: '浓重的烟灰和火药颗粒集中在射入孔周围,形成明显但仍紧凑的径向晕环。',
|
|
88
|
+
expectedCloseRange: '火药纹身和金属残留物加宽为可测量的环;热效应和烟灰变得微不足道。',
|
|
89
|
+
expectedDistant: '颗粒非常稀疏且分布广泛。确定射击距离需要化学检验、显微镜观察和比较试射。',
|
|
90
|
+
contact: '接触',
|
|
91
|
+
near: '近接触',
|
|
92
|
+
close: '近距离',
|
|
93
|
+
distant: '远距离',
|
|
94
|
+
disclaimer: '仅供教学展示。使用 GSR 判定射击距离需要进行控制条件下的比对试射、目标物质比对以及经验证的化学分析方法。',
|
|
95
|
+
},
|
|
96
|
+
seo: [
|
|
97
|
+
{
|
|
98
|
+
type: 'title',
|
|
99
|
+
text: '根据 GSR 射击残留物分布估算射击距离',
|
|
100
|
+
level: 2,
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
type: 'diagnostic',
|
|
104
|
+
variant: 'info',
|
|
105
|
+
icon: 'mdi:target-variant',
|
|
106
|
+
badge: '教学模型',
|
|
107
|
+
title: '该计算器解答什么',
|
|
108
|
+
html: '此 GSR 扩散计算器展示残留物分布是更像接触射击、近接触射击、近距离射击,还是远距离射击。它是为了辅助学习 <strong>使用射击残留物判定射击距离</strong> 的科学原理,而不是为了出具具体的法医鉴定意见。',
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
type: 'stats',
|
|
112
|
+
columns: 4,
|
|
113
|
+
items: [
|
|
114
|
+
{ value: '0-3 米', label: '模拟的枪口到目标距离', icon: 'mdi:ruler' },
|
|
115
|
+
{ value: '4', label: '包含的射击距离类别', icon: 'mdi:crosshairs-question' },
|
|
116
|
+
{ value: '3', label: '比对的弹药配置文件', icon: 'mdi:ammunition' },
|
|
117
|
+
{ value: '2', label: '单位系统:公制和英制', icon: 'mdi:swap-horizontal' },
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
type: 'title',
|
|
122
|
+
text: '如何解读模拟的分布图样',
|
|
123
|
+
level: 3,
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
type: 'summary',
|
|
127
|
+
title: '快速判读清单',
|
|
128
|
+
items: [
|
|
129
|
+
'中心高密度烟灰、烧灼、撕裂或枪口印痕提示接触或近接触射击。',
|
|
130
|
+
'紧凑的黑色中心四周分布有火药颗粒提示极近距离,需要试射以精确判定范围。',
|
|
131
|
+
'扩散的火药纹身且几乎无烟灰,通常符合热效应消失后的近距离射击。',
|
|
132
|
+
'在远距离时没有肉眼可见的残留物,并不证明不存在 GSR;化学提取或显微镜观察可能仍会发现微粒。',
|
|
133
|
+
],
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
type: 'comparative',
|
|
137
|
+
columns: 2,
|
|
138
|
+
items: [
|
|
139
|
+
{
|
|
140
|
+
title: '本工具的用途',
|
|
141
|
+
icon: 'mdi:check-circle-outline',
|
|
142
|
+
highlight: true,
|
|
143
|
+
description: '图形化展示了射击距离、残留物密度、颗粒扩散和热效应之间的相互关系。',
|
|
144
|
+
points: ['课堂教学演示', '技术搜索意图解答', '法医学及科学物证词汇', '公制和英制单位比对'],
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
title: '本模拟的局限性',
|
|
148
|
+
icon: 'mdi:alert-circle-outline',
|
|
149
|
+
description: '在没有实验室对实物进行检验的情况下,无法判定具体案件中的真实射击距离。',
|
|
150
|
+
points: ['无法分析案件实物', '无化学定性试验', '未使用涉案枪支进行试射', '无司法证明效力'],
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
type: 'table',
|
|
156
|
+
headers: ['常见疑问', '分布中的关键线索', '技术解答'],
|
|
157
|
+
rows: [
|
|
158
|
+
['接触射击是什么样的?', '重度烟灰、烧灼、裂创或枪口印痕。', '接触射击的判断可能性高,但发射气体膨胀和目标物弹性会严重影响外观。'],
|
|
159
|
+
['枪支距离目标多远?', '入射口附近有高密度残留物并伴有紧凑的火药晕。', '分布图样支持估算距离范围,但必须与比对试射数据相核对。'],
|
|
160
|
+
['什么是火药纹身(Tattooing)?', '未燃烧火药颗粒撞击造成的点状损伤和极少量的烟灰。', '其扩散程度受弹药类型、目标织物、射击角度和中间障碍物影响。'],
|
|
161
|
+
['远距离时残留物会彻底消失吗?', '微粒极度分散,肉眼无明显宏观沉积。', '视觉特征显著减少,但底火微粒依然可以通过实验室分析检出。'],
|
|
162
|
+
],
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
type: 'title',
|
|
166
|
+
text: '模拟模型的物理科学原理',
|
|
167
|
+
level: 3,
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
type: 'paragraph',
|
|
171
|
+
html: '本模拟器采用高斯径向分布在冲击中心周围随机分布微粒。短距离产生狭窄且高密度的沉积;距离增加导致扩散范围扩大且平均密度降低。模型将热烟灰与金属底火颗粒分开计算,因为热烟灰的物理达到距离远短于微观金属颗粒的散落范围。',
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
type: 'glossary',
|
|
175
|
+
items: [
|
|
176
|
+
{ term: 'GSR', definition: '射击残留物(Gunshot Residue):发射时由底火、火药、弹头和高温气体产生的所有颗粒和化合物。' },
|
|
177
|
+
{ term: '烟灰发黑', definition: '富含碳的黑色沉积物,通常仅在接触和极近距离射击时肉眼可见。' },
|
|
178
|
+
{ term: '火药纹身', definition: '未燃烧或部分燃烧的火药颗粒撞击目标表面产生的微小点状痕迹或损伤。' },
|
|
179
|
+
{ term: '枪口印痕', definition: '接触射击时由于高压气体和热量将枪口形状烙印在目标表面形成的痕迹。' },
|
|
180
|
+
{ term: '比对试射', definition: '使用涉案枪支和同批次弹药在已知距离下对相同目标材料进行的实验发射。' },
|
|
181
|
+
],
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
type: 'title',
|
|
185
|
+
text: '法医学射击距离类别和预期所见',
|
|
186
|
+
level: 3,
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
type: 'table',
|
|
190
|
+
headers: ['射击类别', '常见物理特征', '鉴定解读限制'],
|
|
191
|
+
rows: [
|
|
192
|
+
['接触', '中心大量沉积、烟灰、烧灼、破裂、枪口印痕。', '织物的弹性和气体压力可能会极大地改变孔洞的直径形态。'],
|
|
193
|
+
['近接触', '射入孔周围有致密的烟灰和紧凑的火药晕。', '宽松的衣物或中间障碍物可能会阻挡预期的残留物分布。'],
|
|
194
|
+
['近距离', '无明显烟灰沉积的火药纹身和扩散微粒。', '相同的距离在棉织物、牛仔布、皮肤或纸张上显示不同的分布图样。'],
|
|
195
|
+
['远距离', '无肉眼可见痕迹,或仅有宽范围散在的微观颗粒。', '当肉眼特征消失时,化学显色法和扫描电镜能谱(SEM-EDS)分析至关重要。'],
|
|
196
|
+
],
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
type: 'tip',
|
|
200
|
+
title: '实务解读提示',
|
|
201
|
+
html: '评估射击残留物时应始终将其作为一个 <strong>范围比对问题</strong>,绝不能断定为单一的精确数值。科学严谨的结论只能来自于将送检物证与在相同条件下获取的试射样片进行直接比对。',
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
type: 'title',
|
|
205
|
+
text: '分布图样分析前的文档记录规程',
|
|
206
|
+
level: 3,
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
type: 'list',
|
|
210
|
+
icon: 'mdi:clipboard-check-outline',
|
|
211
|
+
items: [
|
|
212
|
+
'记录目标织物的物理特性(织物类型、颜色、厚度、弹性以及是否有折叠)。',
|
|
213
|
+
'记录枪支的制造厂商、型号、枪管长度、弹药批号、火药类型和枪口装置。',
|
|
214
|
+
'拍摄分布图样,照片中必须包含比例尺、照明方向和物证链记录。',
|
|
215
|
+
'分别分析烟灰、烧灼、未燃火药颗粒、擦拭圈和织物撕裂创。',
|
|
216
|
+
'在正式司法鉴定出具具体射击距离之前,必须先进行控制条件下的比对试射。',
|
|
217
|
+
],
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
type: 'proscons',
|
|
221
|
+
title: '数字化模拟与实验室弹道检验的对比',
|
|
222
|
+
items: [
|
|
223
|
+
{ pro: '能够直观地帮助理解射击残留物颗粒如何随着距离增加而扩散。', con: '无法复制特定枪支个体、弹药批次或目标织物的特定理化特征。' },
|
|
224
|
+
{ pro: '理清并明确了各个射击距离类别之间的概念差异。', con: '无法替代化学显色试验或扫描电子显微镜(SEM-EDS)分析。' },
|
|
225
|
+
{ pro: '帮助认识肉眼视觉宏观分析的物理局限性。', con: '在没有实验室实验验证和有资质的鉴定官评估前,不具备法庭证据效力。' },
|
|
226
|
+
],
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
type: 'diagnostic',
|
|
230
|
+
variant: 'warning',
|
|
231
|
+
icon: 'mdi:scale-balance',
|
|
232
|
+
badge: '司法鉴定警告',
|
|
233
|
+
title: '排除法庭和诉讼目的使用',
|
|
234
|
+
html: '计算机模拟无法替代真实案件的物证鉴定。它不能评估枪支维护状态、火药化学特征、中间障碍物或检样提取质量。本页面仅供教学参考,真实案件请联系经国家认可的专业司法鉴定机构。',
|
|
235
|
+
},
|
|
236
|
+
],
|
|
237
|
+
faq,
|
|
238
|
+
bibliography,
|
|
239
|
+
howTo,
|
|
240
|
+
schemas: [
|
|
241
|
+
{
|
|
242
|
+
'@context': 'https://schema.org',
|
|
243
|
+
'@type': 'SoftwareApplication',
|
|
244
|
+
name: title,
|
|
245
|
+
description,
|
|
246
|
+
applicationCategory: 'ForensicApplication',
|
|
247
|
+
operatingSystem: 'Any',
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
'@context': 'https://schema.org',
|
|
251
|
+
'@type': 'FAQPage',
|
|
252
|
+
mainEntity: faq.map((item) => ({
|
|
253
|
+
'@type': 'Question',
|
|
254
|
+
name: item.question,
|
|
255
|
+
acceptedAnswer: {
|
|
256
|
+
'@type': 'Answer',
|
|
257
|
+
text: item.answer,
|
|
258
|
+
},
|
|
259
|
+
})),
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
'@context': 'https://schema.org',
|
|
263
|
+
'@type': 'HowTo',
|
|
264
|
+
name: title,
|
|
265
|
+
step: howTo.map((step) => ({
|
|
266
|
+
'@type': 'HowToStep',
|
|
267
|
+
name: step.name,
|
|
268
|
+
text: step.text,
|
|
269
|
+
})),
|
|
270
|
+
},
|
|
271
|
+
],
|
|
272
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { gsrDispersionCalculator } from './entry';
|
|
2
|
+
import type { ToolDefinition } from '../../types';
|
|
3
|
+
|
|
4
|
+
export * from './entry';
|
|
5
|
+
|
|
6
|
+
export const GSR_DISPERSION_CALCULATOR_TOOL: ToolDefinition = {
|
|
7
|
+
entry: gsrDispersionCalculator,
|
|
8
|
+
Component: () => import('./component.astro'),
|
|
9
|
+
SEOComponent: () => import('./seo.astro'),
|
|
10
|
+
BibliographyComponent: () => import('./bibliography.astro'),
|
|
11
|
+
};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
export type AmmoType = 'pistol9mm' | 'revolver38' | 'shotgun12';
|
|
2
|
+
|
|
3
|
+
export interface AmmoProfile {
|
|
4
|
+
id: AmmoType;
|
|
5
|
+
label: string;
|
|
6
|
+
baseParticles: number;
|
|
7
|
+
spreadMultiplier: number;
|
|
8
|
+
residueStrength: number;
|
|
9
|
+
thermalReachCm: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface GsrParticle {
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
radius: number;
|
|
16
|
+
opacity: number;
|
|
17
|
+
element: 'lead' | 'barium' | 'antimony' | 'soot';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface GsrSimulation {
|
|
21
|
+
distanceCm: number;
|
|
22
|
+
category: 'contact' | 'near-contact' | 'close-range' | 'distant';
|
|
23
|
+
patternRadiusCm: number;
|
|
24
|
+
densityScore: number;
|
|
25
|
+
thermalScore: number;
|
|
26
|
+
particles: GsrParticle[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface ParticleFieldConfig {
|
|
30
|
+
count: number;
|
|
31
|
+
spread: number;
|
|
32
|
+
thermalScore: number;
|
|
33
|
+
densityScore: number;
|
|
34
|
+
seed: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const AMMO_PROFILES: Record<AmmoType, AmmoProfile> = {
|
|
38
|
+
pistol9mm: {
|
|
39
|
+
id: 'pistol9mm',
|
|
40
|
+
label: 'pistol9mm',
|
|
41
|
+
baseParticles: 360,
|
|
42
|
+
spreadMultiplier: 1,
|
|
43
|
+
residueStrength: 1,
|
|
44
|
+
thermalReachCm: 35,
|
|
45
|
+
},
|
|
46
|
+
revolver38: {
|
|
47
|
+
id: 'revolver38',
|
|
48
|
+
label: 'revolver38',
|
|
49
|
+
baseParticles: 430,
|
|
50
|
+
spreadMultiplier: 1.18,
|
|
51
|
+
residueStrength: 1.12,
|
|
52
|
+
thermalReachCm: 42,
|
|
53
|
+
},
|
|
54
|
+
shotgun12: {
|
|
55
|
+
id: 'shotgun12',
|
|
56
|
+
label: 'shotgun12',
|
|
57
|
+
baseParticles: 520,
|
|
58
|
+
spreadMultiplier: 1.65,
|
|
59
|
+
residueStrength: 1.24,
|
|
60
|
+
thermalReachCm: 55,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
function seededRandom(seed: number): () => number {
|
|
65
|
+
let state = seed >>> 0;
|
|
66
|
+
return () => {
|
|
67
|
+
state = (state * 1664525 + 1013904223) >>> 0;
|
|
68
|
+
return state / 4294967296;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function gaussian(random: () => number): number {
|
|
73
|
+
const first = Math.max(random(), 0.0001);
|
|
74
|
+
const second = random();
|
|
75
|
+
return Math.sqrt(-2 * Math.log(first)) * Math.cos(2 * Math.PI * second);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function getCategory(distanceCm: number): GsrSimulation['category'] {
|
|
79
|
+
if (distanceCm <= 3) return 'contact';
|
|
80
|
+
if (distanceCm <= 30) return 'near-contact';
|
|
81
|
+
if (distanceCm <= 120) return 'close-range';
|
|
82
|
+
return 'distant';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function getSpread(distanceCm: number, profile: AmmoProfile): number {
|
|
86
|
+
return Math.max(2, (distanceCm * 0.18 + 2.6) * profile.spreadMultiplier);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function getDensityScore(distanceCm: number, profile: AmmoProfile): number {
|
|
90
|
+
return Math.round(Math.max(4, Math.min(100, (profile.residueStrength * 100) / (1 + distanceCm / 22))));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function getThermalScore(distanceCm: number, profile: AmmoProfile): number {
|
|
94
|
+
return Math.round(Math.max(0, 100 * (1 - distanceCm / profile.thermalReachCm)));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function getVisibleParticles(profile: AmmoProfile, densityScore: number): number {
|
|
98
|
+
return Math.round(Math.max(70, Math.min(620, profile.baseParticles * (0.42 + densityScore / 170))));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function createParticles(config: ParticleFieldConfig): GsrParticle[] {
|
|
102
|
+
const { count, spread, thermalScore, densityScore, seed } = config;
|
|
103
|
+
const random = seededRandom(seed);
|
|
104
|
+
const particles: GsrParticle[] = [];
|
|
105
|
+
const elements: GsrParticle['element'][] = ['lead', 'barium', 'antimony', 'soot'];
|
|
106
|
+
|
|
107
|
+
for (let index = 0; index < count; index++) {
|
|
108
|
+
const isSoot = random() < Math.max(0.04, thermalScore / 210);
|
|
109
|
+
const angle = random() * Math.PI * 2;
|
|
110
|
+
const radial = Math.abs(gaussian(random)) * spread * (isSoot ? 0.55 : 1);
|
|
111
|
+
particles.push({
|
|
112
|
+
x: Math.cos(angle) * radial,
|
|
113
|
+
y: Math.sin(angle) * radial,
|
|
114
|
+
radius: isSoot ? 1.8 + random() * 4 : 0.9 + random() * 2.8,
|
|
115
|
+
opacity: Math.max(0.16, Math.min(0.92, (densityScore / 100) * (0.45 + random() * 0.65))),
|
|
116
|
+
element: isSoot ? 'soot' : elements[Math.floor(random() * 3)],
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return particles;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export class GsrDispersionModel {
|
|
124
|
+
simulate(distanceCm: number, ammoType: AmmoType): GsrSimulation {
|
|
125
|
+
const profile = AMMO_PROFILES[ammoType] ?? AMMO_PROFILES.pistol9mm;
|
|
126
|
+
const category = getCategory(distanceCm);
|
|
127
|
+
const spread = getSpread(distanceCm, profile);
|
|
128
|
+
const densityScore = getDensityScore(distanceCm, profile);
|
|
129
|
+
const thermalScore = getThermalScore(distanceCm, profile);
|
|
130
|
+
const visibleParticles = getVisibleParticles(profile, densityScore);
|
|
131
|
+
const particles = createParticles({
|
|
132
|
+
count: visibleParticles,
|
|
133
|
+
spread,
|
|
134
|
+
thermalScore,
|
|
135
|
+
densityScore,
|
|
136
|
+
seed: Math.round(distanceCm * 97) + profile.baseParticles,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
distanceCm,
|
|
141
|
+
category,
|
|
142
|
+
patternRadiusCm: Math.round(spread * 2.8),
|
|
143
|
+
densityScore,
|
|
144
|
+
thermalScore,
|
|
145
|
+
particles,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { SEORenderer } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { gsrDispersionCalculator } from './index';
|
|
4
|
+
import type { KnownLocale } from '../../types';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
locale?: KnownLocale;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { locale = 'en' } = Astro.props;
|
|
11
|
+
const content = await gsrDispersionCalculator.i18n[locale]?.();
|
|
12
|
+
if (!content) return null;
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
{content.seo && content.seo.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { Bibliography as SharedBibliography } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { widmarkAlcoholSimulator } from './index';
|
|
4
|
+
import type { KnownLocale } from '../../types';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
locale?: KnownLocale;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { locale = 'en' } = Astro.props;
|
|
11
|
+
const content = await widmarkAlcoholSimulator.i18n[locale]?.();
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
{content && <SharedBibliography links={content.bibliography} />}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { BibliographyEntry } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const bibliography: BibliographyEntry[] = [
|
|
4
|
+
{
|
|
5
|
+
name: 'Widmark EMP. Principles and Applications of Medicolegal Alcohol Determination. Biomedical Publications.',
|
|
6
|
+
url: 'https://analyticalsciencejournals.onlinelibrary.wiley.com/doi/10.1002/jat.2550020515',
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
name: 'Jones AW. Pharmacokinetics of ethanol in saliva: comparison with blood and breath. Clinical Pharmacokinetics.',
|
|
10
|
+
url: 'https://pubmed.ncbi.nlm.nih.gov/8375057/',
|
|
11
|
+
}
|
|
12
|
+
];
|