@jjlmoya/utils-science 1.20.0 → 1.22.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 +2 -1
- package/src/category/i18n/de.ts +1 -1
- package/src/category/i18n/fr.ts +6 -6
- package/src/category/i18n/ru.ts +1 -1
- package/src/category/index.ts +4 -1
- package/src/category/seo.astro +2 -2
- package/src/entries.ts +7 -1
- package/src/index.ts +3 -0
- package/src/pages/[locale]/[slug].astro +5 -4
- package/src/tests/locale_completeness.test.ts +2 -2
- package/src/tests/no_en_dash.test.ts +70 -0
- package/src/tests/seo_length.test.ts +5 -3
- package/src/tests/title_quality.test.ts +1 -1
- package/src/tests/tool_validation.test.ts +2 -2
- package/src/tool/asteroid-impact/bibliography.astro +2 -2
- package/src/tool/asteroid-impact/component.astro +16 -9
- package/src/tool/asteroid-impact/i18n/fr.ts +6 -6
- package/src/tool/asteroid-impact/i18n/ru.ts +4 -4
- package/src/tool/asteroid-impact/index.ts +1 -0
- package/src/tool/asteroid-impact/script.ts +13 -7
- package/src/tool/cellular-renewal/bibliography.astro +2 -2
- package/src/tool/cellular-renewal/i18n/fr.ts +13 -13
- package/src/tool/cellular-renewal/i18n/ru.ts +17 -17
- package/src/tool/cellular-renewal/i18n/zh.ts +9 -9
- package/src/tool/cellular-renewal/index.ts +1 -0
- package/src/tool/colony-counter/bibliography.astro +2 -2
- package/src/tool/colony-counter/i18n/ru.ts +5 -5
- package/src/tool/colony-counter/i18n/zh.ts +2 -2
- package/src/tool/colony-counter/index.ts +1 -0
- package/src/tool/cosmic-inflation/bibliography.astro +14 -0
- package/src/tool/cosmic-inflation/bibliography.ts +12 -0
- package/src/tool/cosmic-inflation/component.astro +270 -0
- package/src/tool/cosmic-inflation/cosmic-inflation-calculator.css +277 -0
- package/src/tool/cosmic-inflation/entry.ts +26 -0
- package/src/tool/cosmic-inflation/i18n/de.ts +188 -0
- package/src/tool/cosmic-inflation/i18n/en.ts +188 -0
- package/src/tool/cosmic-inflation/i18n/es.ts +168 -0
- package/src/tool/cosmic-inflation/i18n/fr.ts +188 -0
- package/src/tool/cosmic-inflation/i18n/id.ts +188 -0
- package/src/tool/cosmic-inflation/i18n/it.ts +188 -0
- package/src/tool/cosmic-inflation/i18n/ja.ts +188 -0
- package/src/tool/cosmic-inflation/i18n/ko.ts +188 -0
- package/src/tool/cosmic-inflation/i18n/nl.ts +188 -0
- package/src/tool/cosmic-inflation/i18n/pl.ts +188 -0
- package/src/tool/cosmic-inflation/i18n/pt.ts +188 -0
- package/src/tool/cosmic-inflation/i18n/ru.ts +188 -0
- package/src/tool/cosmic-inflation/i18n/sv.ts +188 -0
- package/src/tool/cosmic-inflation/i18n/tr.ts +188 -0
- package/src/tool/cosmic-inflation/i18n/zh.ts +188 -0
- package/src/tool/cosmic-inflation/index.ts +11 -0
- package/src/tool/cosmic-inflation/logic/CosmicInflationEngine.ts +21 -0
- package/src/tool/cosmic-inflation/seo.astro +15 -0
- package/src/tool/lorenz-attractor/bibliography.astro +14 -0
- package/src/tool/lorenz-attractor/bibliography.ts +12 -0
- package/src/tool/lorenz-attractor/component.astro +146 -0
- package/src/tool/lorenz-attractor/entry.ts +27 -0
- package/src/tool/lorenz-attractor/i18n/de.ts +113 -0
- package/src/tool/lorenz-attractor/i18n/en.ts +185 -0
- package/src/tool/lorenz-attractor/i18n/es.ts +113 -0
- package/src/tool/lorenz-attractor/i18n/fr.ts +113 -0
- package/src/tool/lorenz-attractor/i18n/id.ts +113 -0
- package/src/tool/lorenz-attractor/i18n/it.ts +113 -0
- package/src/tool/lorenz-attractor/i18n/ja.ts +113 -0
- package/src/tool/lorenz-attractor/i18n/ko.ts +113 -0
- package/src/tool/lorenz-attractor/i18n/nl.ts +113 -0
- package/src/tool/lorenz-attractor/i18n/pl.ts +113 -0
- package/src/tool/lorenz-attractor/i18n/pt.ts +113 -0
- package/src/tool/lorenz-attractor/i18n/ru.ts +113 -0
- package/src/tool/lorenz-attractor/i18n/sv.ts +113 -0
- package/src/tool/lorenz-attractor/i18n/tr.ts +113 -0
- package/src/tool/lorenz-attractor/i18n/zh.ts +113 -0
- package/src/tool/lorenz-attractor/index.ts +9 -0
- package/src/tool/lorenz-attractor/logic/LorenzEngine.ts +32 -0
- package/src/tool/lorenz-attractor/lorenz-attractor.css +335 -0
- package/src/tool/lorenz-attractor/renderer.ts +136 -0
- package/src/tool/lorenz-attractor/script.ts +282 -0
- package/src/tool/lorenz-attractor/seo.astro +15 -0
- package/src/tool/microwave-detector/bibliography.astro +2 -2
- package/src/tool/microwave-detector/component.astro +9 -7
- package/src/tool/microwave-detector/i18n/fr.ts +4 -4
- package/src/tool/microwave-detector/i18n/ru.ts +18 -18
- package/src/tool/microwave-detector/i18n/zh.ts +10 -10
- package/src/tool/microwave-detector/index.ts +1 -0
- package/src/tool/microwave-detector/logic/MicrowaveEngine.ts +5 -1
- package/src/tool/simulation-probability/bibliography.astro +2 -2
- package/src/tool/simulation-probability/i18n/fr.ts +5 -5
- package/src/tool/simulation-probability/i18n/ru.ts +7 -7
- package/src/tool/simulation-probability/i18n/zh.ts +4 -4
- package/src/tool/simulation-probability/index.ts +1 -0
- package/src/tool/temperature-timeline/bibliography.astro +14 -0
- package/src/tool/temperature-timeline/bibliography.ts +12 -0
- package/src/tool/temperature-timeline/component.astro +289 -0
- package/src/tool/temperature-timeline/entry.ts +26 -0
- package/src/tool/temperature-timeline/i18n/de.ts +213 -0
- package/src/tool/temperature-timeline/i18n/en.ts +213 -0
- package/src/tool/temperature-timeline/i18n/es.ts +178 -0
- package/src/tool/temperature-timeline/i18n/fr.ts +213 -0
- package/src/tool/temperature-timeline/i18n/id.ts +213 -0
- package/src/tool/temperature-timeline/i18n/it.ts +213 -0
- package/src/tool/temperature-timeline/i18n/ja.ts +213 -0
- package/src/tool/temperature-timeline/i18n/ko.ts +213 -0
- package/src/tool/temperature-timeline/i18n/nl.ts +213 -0
- package/src/tool/temperature-timeline/i18n/pl.ts +213 -0
- package/src/tool/temperature-timeline/i18n/pt.ts +213 -0
- package/src/tool/temperature-timeline/i18n/ru.ts +213 -0
- package/src/tool/temperature-timeline/i18n/sv.ts +213 -0
- package/src/tool/temperature-timeline/i18n/tr.ts +213 -0
- package/src/tool/temperature-timeline/i18n/zh.ts +213 -0
- package/src/tool/temperature-timeline/index.ts +11 -0
- package/src/tool/temperature-timeline/logic/TemperatureTimelineEngine.ts +58 -0
- package/src/tool/temperature-timeline/planet-temperature-timeline.css +158 -0
- package/src/tool/temperature-timeline/seo.astro +15 -0
- package/src/tools.ts +6 -0
- package/src/types.ts +1 -1
|
@@ -22,7 +22,7 @@ const howTo = [
|
|
|
22
22
|
const faq = [
|
|
23
23
|
{
|
|
24
24
|
question: '网站是如何检测我的微波炉的?',
|
|
25
|
-
answer: '我们不使用神奇的传感器,而是利用网络延迟。2.4GHz WiFi 和微波炉都在相同的频率(约 2450 MHz)下工作。如果微波炉屏蔽失效,它会产生与 WiFi
|
|
25
|
+
answer: '我们不使用神奇的传感器,而是利用网络延迟。2.4GHz WiFi 和微波炉都在相同的频率(约 2450 MHz)下工作。如果微波炉屏蔽失效,它会产生与 WiFi 冲突的"噪声",从而剧烈增加延迟(Ping)。',
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
28
|
question: '如果我的微波炉有泄漏,危险吗?',
|
|
@@ -34,7 +34,7 @@ const faq = [
|
|
|
34
34
|
},
|
|
35
35
|
{
|
|
36
36
|
question: '图表上的延迟尖峰意味着什么?',
|
|
37
|
-
answer: '
|
|
37
|
+
answer: '微波炉运行期间持续出现的尖峰表明电磁信号正在"逃逸"并使空气饱和,导致 WiFi 数据包无法按时送达。',
|
|
38
38
|
},
|
|
39
39
|
];
|
|
40
40
|
import { bibliography } from '../bibliography';
|
|
@@ -97,11 +97,11 @@ export const content: ToolLocaleContent = {
|
|
|
97
97
|
},
|
|
98
98
|
{
|
|
99
99
|
type: 'paragraph',
|
|
100
|
-
html: '任何在 2.4GHz 频段运行的 WiFi
|
|
100
|
+
html: '任何在 2.4GHz 频段运行的 WiFi 设备都在与您的微波炉争夺同一片"空间"。在一个完美的微波炉中,<strong>法拉第笼</strong>(即您在炉门和内壁上看到的金属网)应 100% 阻隔能量。',
|
|
101
101
|
},
|
|
102
102
|
{
|
|
103
103
|
type: 'paragraph',
|
|
104
|
-
html: '然而,长远来看,没有任何屏蔽笼是完美的。炉门密封条、磁性搭扣和金属磨损都可能导致少量能量逸出。虽然这些水平通常远低于人体安全限值,但对于 WiFi 网卡来说却是<strong>巨大</strong
|
|
104
|
+
html: '然而,长远来看,没有任何屏蔽笼是完美的。炉门密封条、磁性搭扣和金属磨损都可能导致少量能量逸出。虽然这些水平通常远低于人体安全限值,但对于 WiFi 网卡来说却是<strong>巨大</strong>的干扰,会导致延迟、丢包以及我们工具测量的特征性"抖动"。',
|
|
105
105
|
},
|
|
106
106
|
{
|
|
107
107
|
type: 'title',
|
|
@@ -116,7 +116,7 @@ export const content: ToolLocaleContent = {
|
|
|
116
116
|
type: 'list',
|
|
117
117
|
items: [
|
|
118
118
|
'<strong>步骤 1 - 手机:</strong> 将手机放入微波炉内(<strong>切记严禁开启微波炉!</strong> 只需关上炉门即可)。',
|
|
119
|
-
'<strong>步骤 2 - 拨打:</strong>
|
|
119
|
+
'<strong>步骤 2 - 拨打:</strong> 尝试从另一台设备拨打该手机。如果手机响了,说明法拉第笼有缺陷或未针对蜂窝频率进行优化。如果提示"不在服务区",则说明屏蔽工作正常。',
|
|
120
120
|
],
|
|
121
121
|
},
|
|
122
122
|
{
|
|
@@ -127,9 +127,9 @@ export const content: ToolLocaleContent = {
|
|
|
127
127
|
{
|
|
128
128
|
type: 'list',
|
|
129
129
|
items: [
|
|
130
|
-
'<strong
|
|
131
|
-
'<strong
|
|
132
|
-
'<strong
|
|
130
|
+
'<strong>"微波会改变水分子的结构"</strong> - 错误。微波辐射是 <em>非电离辐射</em>。它没有足够的能量来打破化学键或改变 DNA。它只是使水分子振动,从而增加其动能(温度)。',
|
|
131
|
+
'<strong>"辐射会累积在食物中"</strong> - 错误。微波就像光:一旦关掉开关,它们就消失了。加热后的食物不会发出微波辐射,只会像营火或平底锅一样发出红外辐射(热量)。',
|
|
132
|
+
'<strong>"盯着转盘看会损伤眼睛"</strong> - 部分属实。炉门玻璃设计有金属网,可以阻隔 2.45GHz 波长的辐射。然而,如果金属网损坏,玻璃将无法阻挡微波。眼睛的晶状体对热量非常敏感且血液循环较差,因此长时间直接暴露(在泄漏点附近)可能会导致热致白内障。',
|
|
133
133
|
],
|
|
134
134
|
},
|
|
135
135
|
{
|
|
@@ -139,7 +139,7 @@ export const content: ToolLocaleContent = {
|
|
|
139
139
|
},
|
|
140
140
|
{
|
|
141
141
|
type: 'paragraph',
|
|
142
|
-
html: '我们的工具并不直接测量辐射(智能手机没有相关传感器),但它测量辐射的<strong>主要症状</strong>。当 2.45GHz 的辐射分子从微波炉逃逸时,它会与您设备的 WiFi
|
|
142
|
+
html: '我们的工具并不直接测量辐射(智能手机没有相关传感器),但它测量辐射的<strong>主要症状</strong>。当 2.45GHz 的辐射分子从微波炉逃逸时,它会与您设备的 WiFi 信号"碰撞"。这会导致:',
|
|
143
143
|
},
|
|
144
144
|
{
|
|
145
145
|
type: 'list',
|
|
@@ -147,7 +147,7 @@ export const content: ToolLocaleContent = {
|
|
|
147
147
|
'<strong>数据包重传:</strong> 路由器检测到消息到达时已损坏,必须重新发送,从而增加了延迟。',
|
|
148
148
|
'<strong>抖动变化:</strong> 这是衡量响应时间不稳定性的指标。高抖动是存在大规模外部干扰的明确信号。',
|
|
149
149
|
'<strong>性能下降:</strong> 由于射频频谱饱和,传输速度骤降。',
|
|
150
|
-
'<strong>背景噪声:</strong>
|
|
150
|
+
'<strong>背景噪声:</strong> 微波炉注入的噪声提高了"噪声底限",使设备难以区分真实数据和电磁混乱。',
|
|
151
151
|
],
|
|
152
152
|
},
|
|
153
153
|
],
|
|
@@ -81,7 +81,11 @@ export class MicrowaveEngine {
|
|
|
81
81
|
if (this.lastPings.length < 2) return 0;
|
|
82
82
|
let diffSum = 0;
|
|
83
83
|
for (let i = 1; i < this.lastPings.length; i++) {
|
|
84
|
-
|
|
84
|
+
const current = this.lastPings[i];
|
|
85
|
+
const prev = this.lastPings[i - 1];
|
|
86
|
+
if (current !== undefined && prev !== undefined) {
|
|
87
|
+
diffSum += Math.abs(current - prev);
|
|
88
|
+
}
|
|
85
89
|
}
|
|
86
90
|
return diffSum / (this.lastPings.length - 1);
|
|
87
91
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { Bibliography } from '@jjlmoya/utils-shared';
|
|
2
|
+
import { Bibliography as SharedBibliography } from '@jjlmoya/utils-shared';
|
|
3
3
|
import { simulationProbability } from './index';
|
|
4
4
|
import type { KnownLocale } from '../../types';
|
|
5
5
|
|
|
@@ -11,4 +11,4 @@ const { locale = 'es' } = Astro.props;
|
|
|
11
11
|
const content = await simulationProbability.i18n[locale]?.();
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
{content && <
|
|
14
|
+
{content && <SharedBibliography links={content.bibliography} />}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const description = 'Analysez si notre réalité est une simulation en utilisant l\'argument du trilemme de Nick Bostrom. Calculez la probabilité existentielle avec 4 paramètres clés.';
|
|
2
2
|
const slug = 'calculateur-probabilite-simulation';
|
|
3
|
-
const title = 'Calculateur de Probabilité de Simulation
|
|
3
|
+
const title = 'Calculateur de Probabilité de Simulation: Vivez vous dans un univers virtuel ?';
|
|
4
4
|
const howTo = [
|
|
5
5
|
{
|
|
6
6
|
name: 'Ajuster le progrès technologique',
|
|
@@ -12,7 +12,7 @@ const howTo = [
|
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
14
|
name: 'Évaluer l\'intérêt pour la simulation',
|
|
15
|
-
text: 'Si l\'humanité atteignait le niveau post-humain, voudrait-elle créer des simulations de son passé ? Considérez les motivations
|
|
15
|
+
text: 'Si l\'humanité atteignait le niveau post-humain, voudrait-elle créer des simulations de son passé ? Considérez les motivations: recherche scientifique, divertissement, préservation historique.',
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
18
|
name: 'Interpréter le résultat',
|
|
@@ -64,9 +64,9 @@ export const content: ToolLocaleContent = {
|
|
|
64
64
|
nSub: 'Échelle de Simulation',
|
|
65
65
|
probabilityTitle: 'Probabilité de Simulation',
|
|
66
66
|
trilemmaResult: 'Verdict du Trilemme',
|
|
67
|
-
scenario1: 'Scénario 1
|
|
68
|
-
scenario2: 'Scénario 2
|
|
69
|
-
scenario3: 'Scénario 3
|
|
67
|
+
scenario1: 'Scénario 1: Extinction',
|
|
68
|
+
scenario2: 'Scénario 2: Désintérêt',
|
|
69
|
+
scenario3: 'Scénario 3: Nous sommes simulés',
|
|
70
70
|
fpDescription: 'Probabilité que l\'humanité atteigne la capacité technique de simuler des univers avec conscience.',
|
|
71
71
|
flDescription: 'Probabilité d\'éviter l\'effondrement (extinction, guerre) avant d\'atteindre le niveau post-humain.',
|
|
72
72
|
fiDescription: 'Pourcentage de civilisations avancées qui décident de créer des simulations de leurs ancêtres.',
|
|
@@ -4,7 +4,7 @@ const title = 'Калькулятор вероятности симуляции:
|
|
|
4
4
|
const howTo = [
|
|
5
5
|
{
|
|
6
6
|
name: 'Настройте технологический прогресс',
|
|
7
|
-
text: 'Оцените вероятность того, что человечество достигнет постчеловеческого уровня. 50%
|
|
7
|
+
text: 'Оцените вероятность того, что человечество достигнет постчеловеческого уровня. 50% - нейтрально; увеличивайте, если верите, что ИИ и вычисления продолжат расти экспоненциально.',
|
|
8
8
|
},
|
|
9
9
|
{
|
|
10
10
|
name: 'Установите выживаемость',
|
|
@@ -29,16 +29,16 @@ const faq = [
|
|
|
29
29
|
answer: 'Нет. Высокие вероятности говорят о том, что это серьезная возможность, которую следует учитывать, но это не доказательство. Аргумент основан на предположениях о будущем технологий, которые могут и не реализоваться.',
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
|
-
question: 'Как бы на нас повлияло знание того, что мы
|
|
32
|
+
question: 'Как бы на нас повлияло знание того, что мы - симуляция?',
|
|
33
33
|
answer: 'Философски многие утверждают, что это ничего не меняет по существу. Если вы и ваши близкие симулируетесь вместе, ваши переживания, эмоции и отношения остаются реальными и ощущаемыми. Важен прожитый опыт, а не субстрат.',
|
|
34
34
|
},
|
|
35
35
|
{
|
|
36
36
|
question: 'Есть ли научные доказательства того, что мы в симуляции?',
|
|
37
|
-
answer: 'Убедительных доказательств нет. Некоторые физики указывают на аномалии в квантовой механике (квантование, запутанность) как на возможные
|
|
37
|
+
answer: 'Убедительных доказательств нет. Некоторые физики указывают на аномалии в квантовой механике (квантование, запутанность) как на возможные "вычислительные оптимизации", но это лишь спекулятивные интерпретации.',
|
|
38
38
|
},
|
|
39
39
|
{
|
|
40
|
-
question: 'Какое значение мне следует присвоить
|
|
41
|
-
answer: 'Это зависит от ваших убеждений о будущем вычислительной техники. Низкие значения (100
|
|
40
|
+
question: 'Какое значение мне следует присвоить "N" (масштабу симуляции)?',
|
|
41
|
+
answer: 'Это зависит от ваших убеждений о будущем вычислительной техники. Низкие значения (100-1000) предполагают ограниченность ресурсов. Высокие значения (миллионы) предполагают практически неограниченную вычислительную мощность. В большинстве научных дискуссий используются значения от 1000 до 1 триллиона.',
|
|
42
42
|
},
|
|
43
43
|
];
|
|
44
44
|
import { bibliography } from '../bibliography';
|
|
@@ -81,7 +81,7 @@ export const content: ToolLocaleContent = {
|
|
|
81
81
|
},
|
|
82
82
|
{
|
|
83
83
|
type: 'paragraph',
|
|
84
|
-
html: 'Идея о том, что наша реальность может быть искусственной конструкцией, чрезвычайно продвинутой компьютерной симуляцией, прошла путь от научной фантастики до серьезных философских и научных дебатов. В 2003 году философ Ник Бостром из Оксфордского университета опубликовал статью под названием <em
|
|
84
|
+
html: 'Идея о том, что наша реальность может быть искусственной конструкцией, чрезвычайно продвинутой компьютерной симуляцией, прошла путь от научной фантастики до серьезных философских и научных дебатов. В 2003 году философ Ник Бостром из Оксфордского университета опубликовал статью под названием <em>"Живете ли вы в компьютерной симуляции?"</em>, где предложил аргумент трилеммы, бросающий вызов нашему восприятию бытия.',
|
|
85
85
|
},
|
|
86
86
|
{
|
|
87
87
|
type: 'paragraph',
|
|
@@ -139,7 +139,7 @@ export const content: ToolLocaleContent = {
|
|
|
139
139
|
},
|
|
140
140
|
{
|
|
141
141
|
type: 'paragraph',
|
|
142
|
-
html: 'От квантовой физики до космологии некоторые ученые ищут
|
|
142
|
+
html: 'От квантовой физики до космологии некоторые ученые ищут "глюки" или пределы в разрешении нашей реальности. Если у вселенной есть минимальная длина (Планковская длина) или поведение, похожее на оптимизацию кода (например, квантовая запутанность), гипотеза находит сторонников в области цифровой физики.',
|
|
143
143
|
},
|
|
144
144
|
{
|
|
145
145
|
type: 'title',
|
|
@@ -20,10 +20,10 @@ const faq = [
|
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
22
|
question: '是否有科学证据表明我们处于模拟中?',
|
|
23
|
-
answer: '
|
|
23
|
+
answer: '目前还没有结论性的证据。一些物理学家将量子力学中的异常现象(如量子化、纠缠)视为可能的"计算优化",但这些都是推测性的解释。',
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
|
-
question: '
|
|
26
|
+
question: '我应该给"N"(模拟规模)赋予什么值?',
|
|
27
27
|
answer: '这取决于您对计算机未来的信念。低值(100-1000)假设资源有限。高值(数百万)假设拥有几乎无限的计算能力。大多数科学讨论使用介于 1000 到 1 万亿之间的值。',
|
|
28
28
|
},
|
|
29
29
|
];
|
|
@@ -84,7 +84,7 @@ export const content: ToolLocaleContent = {
|
|
|
84
84
|
},
|
|
85
85
|
{
|
|
86
86
|
type: 'paragraph',
|
|
87
|
-
html: '我们的现实可能是一个人造结构,一个极其先进的计算机模拟,这个想法已经从科幻小说变成了严肃的哲学和科学辩论。2003 年,牛津大学哲学家尼克·波斯特罗姆发表了一篇题为 <em>"你是否生活在计算机模拟中?"</em>
|
|
87
|
+
html: '我们的现实可能是一个人造结构,一个极其先进的计算机模拟,这个想法已经从科幻小说变成了严肃的哲学和科学辩论。2003 年,牛津大学哲学家尼克·波斯特罗姆发表了一篇题为 <em>"你是否生活在计算机模拟中?"</em> 的文章,他在文中提出了一个挑战我们存在认知的"三解困境"论证。',
|
|
88
88
|
},
|
|
89
89
|
{
|
|
90
90
|
type: 'paragraph',
|
|
@@ -141,7 +141,7 @@ export const content: ToolLocaleContent = {
|
|
|
141
141
|
},
|
|
142
142
|
{
|
|
143
143
|
type: 'paragraph',
|
|
144
|
-
html: '
|
|
144
|
+
html: '从量子物理学到宇宙学,一些科学家正在寻找现实分辨率中的"缺陷"或限制。如果宇宙具有最小长度(普朗克长度)或看起来像代码优化(如量子纠缠)的行为,那么该假设在数字物理学领域就会获得支持者。',
|
|
145
145
|
},
|
|
146
146
|
{
|
|
147
147
|
type: 'title',
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { Bibliography as SharedBibliography } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { temperatureTimeline } 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 temperatureTimeline.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: 'Global temperatures in the Cenozoic - Nature Geoscience',
|
|
6
|
+
url: 'https://www.nature.com/nature-index/topics/l4/paleoceanographic-dynamics-of-cenozoic-climate-systems',
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
name: 'Phanerozoic climate history and temperature trends - Science',
|
|
10
|
+
url: 'https://www.science.org/doi/10.1126/science.adk3705',
|
|
11
|
+
},
|
|
12
|
+
];
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
---
|
|
2
|
+
import './planet-temperature-timeline.css';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
ui: Record<string, string>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { ui } = Astro.props;
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<div class="timeline-root" id="temperature-timeline-root" data-ui={JSON.stringify(ui)}>
|
|
12
|
+
<div class="timeline-canvas-container" id="timeline-canvas-area">
|
|
13
|
+
<canvas id="timeline-thermal-canvas"></canvas>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<div class="timeline-ruler-container" id="timeline-ruler">
|
|
17
|
+
<div class="timeline-ruler-track"></div>
|
|
18
|
+
<div class="timeline-ruler-notch" data-index="5">
|
|
19
|
+
<span class="timeline-notch-label">ANT</span>
|
|
20
|
+
<div class="timeline-notch-line"></div>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="timeline-ruler-notch" data-index="4">
|
|
23
|
+
<span class="timeline-notch-label">CEN</span>
|
|
24
|
+
<div class="timeline-notch-line"></div>
|
|
25
|
+
</div>
|
|
26
|
+
<div class="timeline-ruler-notch" data-index="3">
|
|
27
|
+
<span class="timeline-notch-label">MES</span>
|
|
28
|
+
<div class="timeline-notch-line"></div>
|
|
29
|
+
</div>
|
|
30
|
+
<div class="timeline-ruler-notch" data-index="2">
|
|
31
|
+
<span class="timeline-notch-label">PAL</span>
|
|
32
|
+
<div class="timeline-notch-line"></div>
|
|
33
|
+
</div>
|
|
34
|
+
<div class="timeline-ruler-notch" data-index="1">
|
|
35
|
+
<span class="timeline-notch-label">PRO</span>
|
|
36
|
+
<div class="timeline-notch-line"></div>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="timeline-ruler-notch" data-index="0">
|
|
39
|
+
<span class="timeline-notch-label">ARC</span>
|
|
40
|
+
<div class="timeline-notch-line"></div>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div class="timeline-info-panel">
|
|
45
|
+
<div class="timeline-meta-header">
|
|
46
|
+
<span id="timeline-era-age" class="timeline-age">---</span>
|
|
47
|
+
<h3 id="timeline-era-title" class="timeline-era-name">---</h3>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<div class="timeline-temp">
|
|
51
|
+
<span id="timeline-era-temp-val">---</span><span class="timeline-temp-unit">°C</span>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<script>
|
|
57
|
+
import { TemperatureTimelineEngine } from './logic/TemperatureTimelineEngine';
|
|
58
|
+
|
|
59
|
+
const root = document.getElementById('temperature-timeline-root');
|
|
60
|
+
if (root) {
|
|
61
|
+
const ageEl = document.getElementById('timeline-era-age');
|
|
62
|
+
const titleEl = document.getElementById('timeline-era-title');
|
|
63
|
+
const tempValEl = document.getElementById('timeline-era-temp-val');
|
|
64
|
+
const canvas = document.getElementById('timeline-thermal-canvas') as HTMLCanvasElement;
|
|
65
|
+
const canvasArea = document.getElementById('timeline-canvas-area');
|
|
66
|
+
const notches = document.querySelectorAll('.timeline-ruler-notch');
|
|
67
|
+
|
|
68
|
+
const uiData = JSON.parse(root.getAttribute('data-ui') || '{}');
|
|
69
|
+
const engine = new TemperatureTimelineEngine();
|
|
70
|
+
const epochs = engine.getEpochs().map(epoch => ({
|
|
71
|
+
...epoch,
|
|
72
|
+
name: uiData[`epoch_${epoch.id}_name`] || epoch.name,
|
|
73
|
+
ageRange: uiData[`epoch_${epoch.id}_age`] || epoch.ageRange,
|
|
74
|
+
description: uiData[`epoch_${epoch.id}_desc`] || epoch.description,
|
|
75
|
+
}));
|
|
76
|
+
|
|
77
|
+
let currentEraIndex = 4;
|
|
78
|
+
let targetRadius = 130;
|
|
79
|
+
let currentRadius = 130;
|
|
80
|
+
let targetSpeed = 0.015;
|
|
81
|
+
let currentSpeed = 0.015;
|
|
82
|
+
const targetColor = { r: 56, g: 189, b: 248 };
|
|
83
|
+
const currentColor = { r: 56, g: 189, b: 248 };
|
|
84
|
+
let isCold = false;
|
|
85
|
+
|
|
86
|
+
function getThemeConfig(temp: number, isDark: boolean) {
|
|
87
|
+
if (temp >= 25) {
|
|
88
|
+
return { color: { r: 234, g: 88, b: 12 }, radius: 160, speed: 0.04, cold: false };
|
|
89
|
+
} else if (temp >= 20) {
|
|
90
|
+
return { color: { r: 249, g: 115, b: 22 }, radius: 140, speed: 0.03, cold: false };
|
|
91
|
+
} else if (temp >= 15) {
|
|
92
|
+
return { color: { r: 217, g: 119, b: 6 }, radius: 130, speed: 0.02, cold: false };
|
|
93
|
+
} else if (temp >= 13) {
|
|
94
|
+
return { color: isDark ? { r: 56, g: 189, b: 248 } : { r: 2, g: 132, b: 199 }, radius: 110, speed: 0.01, cold: true };
|
|
95
|
+
}
|
|
96
|
+
return { color: isDark ? { r: 224, g: 242, b: 254 } : { r: 15, g: 23, b: 42 }, radius: 90, speed: 0.005, cold: true };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function updateNotchStates(index: number, color: { r: number; g: number; b: number }) {
|
|
100
|
+
notches.forEach(n => {
|
|
101
|
+
const notchIdx = parseInt(n.getAttribute('data-index') || '0');
|
|
102
|
+
const label = n.querySelector('.timeline-notch-label') as HTMLElement;
|
|
103
|
+
if (notchIdx === index) {
|
|
104
|
+
n.classList.add('active');
|
|
105
|
+
if (label) {
|
|
106
|
+
label.style.textShadow = `0 0 12px rgba(${color.r}, ${color.g}, ${color.b}, 0.8)`;
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
n.classList.remove('active');
|
|
110
|
+
if (label) {
|
|
111
|
+
label.style.textShadow = 'none';
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function selectEpoch(index: number) {
|
|
118
|
+
if (index < 0) index = 0;
|
|
119
|
+
if (index >= epochs.length) index = epochs.length - 1;
|
|
120
|
+
currentEraIndex = index;
|
|
121
|
+
|
|
122
|
+
const epoch = epochs[index];
|
|
123
|
+
if (!epoch) return;
|
|
124
|
+
|
|
125
|
+
if (ageEl) ageEl.textContent = epoch.ageRange;
|
|
126
|
+
if (titleEl) titleEl.textContent = epoch.name;
|
|
127
|
+
if (tempValEl) tempValEl.textContent = epoch.avgTemp.toFixed(1);
|
|
128
|
+
|
|
129
|
+
const isDark = document.body.classList.contains('theme-dark') || document.documentElement.classList.contains('theme-dark');
|
|
130
|
+
const config = getThemeConfig(epoch.avgTemp, isDark);
|
|
131
|
+
|
|
132
|
+
targetColor.r = config.color.r;
|
|
133
|
+
targetColor.g = config.color.g;
|
|
134
|
+
targetColor.b = config.color.b;
|
|
135
|
+
targetRadius = config.radius;
|
|
136
|
+
targetSpeed = config.speed;
|
|
137
|
+
isCold = config.cold;
|
|
138
|
+
|
|
139
|
+
updateNotchStates(index, targetColor);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
let isDragging = false;
|
|
143
|
+
let startY = 0;
|
|
144
|
+
let startEraIndex = 4;
|
|
145
|
+
|
|
146
|
+
function handleStart(y: number) {
|
|
147
|
+
isDragging = true;
|
|
148
|
+
startY = y;
|
|
149
|
+
startEraIndex = currentEraIndex;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function handleMove(y: number) {
|
|
153
|
+
if (!isDragging) return;
|
|
154
|
+
const dy = y - startY;
|
|
155
|
+
const height = root?.clientHeight || 600;
|
|
156
|
+
const deltaEra = -Math.round((dy / height) * epochs.length * 1.8);
|
|
157
|
+
let nextIndex = startEraIndex + deltaEra;
|
|
158
|
+
if (nextIndex < 0) nextIndex = 0;
|
|
159
|
+
if (nextIndex >= epochs.length) nextIndex = epochs.length - 1;
|
|
160
|
+
if (nextIndex !== currentEraIndex) {
|
|
161
|
+
selectEpoch(nextIndex);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
canvasArea?.addEventListener('mousedown', (e: MouseEvent) => handleStart(e.clientY));
|
|
166
|
+
window.addEventListener('mousemove', (e: MouseEvent) => handleMove(e.clientY));
|
|
167
|
+
window.addEventListener('mouseup', () => { isDragging = false; });
|
|
168
|
+
|
|
169
|
+
canvasArea?.addEventListener('touchstart', (e: TouchEvent) => {
|
|
170
|
+
if (e.touches[0]) handleStart(e.touches[0].clientY);
|
|
171
|
+
});
|
|
172
|
+
window.addEventListener('touchmove', (e: TouchEvent) => {
|
|
173
|
+
if (e.touches[0]) handleMove(e.touches[0].clientY);
|
|
174
|
+
});
|
|
175
|
+
window.addEventListener('touchend', () => { isDragging = false; });
|
|
176
|
+
|
|
177
|
+
notches.forEach(notch => {
|
|
178
|
+
notch.addEventListener('click', () => {
|
|
179
|
+
const idx = parseInt(notch.getAttribute('data-index') || '0');
|
|
180
|
+
selectEpoch(idx);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const particles: { angle: number; speed: number; radiusOffset: number; size: number }[] = [];
|
|
185
|
+
for (let i = 0; i < 150; i++) {
|
|
186
|
+
particles.push({
|
|
187
|
+
angle: Math.random() * Math.PI * 2,
|
|
188
|
+
speed: 0.005 + Math.random() * 0.015,
|
|
189
|
+
radiusOffset: -40 + Math.random() * 80,
|
|
190
|
+
size: 1.5 + Math.random() * 2.5
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
let phase = 0;
|
|
195
|
+
|
|
196
|
+
function renderParticles(ctx: CanvasRenderingContext2D, cx: number, cy: number, colorString: string) {
|
|
197
|
+
particles.forEach(p => {
|
|
198
|
+
p.angle += p.speed * (currentSpeed * 50 + 0.5);
|
|
199
|
+
|
|
200
|
+
let warp = 0;
|
|
201
|
+
if (!isCold) {
|
|
202
|
+
warp = Math.sin(p.angle * 6 + phase) * 15;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const rad = currentRadius + p.radiusOffset + warp;
|
|
206
|
+
const px = cx + Math.cos(p.angle) * rad;
|
|
207
|
+
const py = cy + Math.sin(p.angle) * rad * 0.7;
|
|
208
|
+
|
|
209
|
+
ctx.beginPath();
|
|
210
|
+
ctx.fillStyle = colorString + `, ${0.15 + (1 - Math.abs(p.radiusOffset) / 80) * 0.65})`;
|
|
211
|
+
ctx.arc(px, py, p.size, 0, Math.PI * 2);
|
|
212
|
+
ctx.fill();
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function renderAtmosphereCircles(ctx: CanvasRenderingContext2D, cx: number, cy: number, colorString: string) {
|
|
217
|
+
if (isCold) {
|
|
218
|
+
ctx.strokeStyle = colorString + ', 0.45)';
|
|
219
|
+
ctx.lineWidth = 1.5;
|
|
220
|
+
for (let i = 1; i < 4; i++) {
|
|
221
|
+
ctx.beginPath();
|
|
222
|
+
ctx.arc(cx, cy, currentRadius * (i / 4), 0, Math.PI * 2);
|
|
223
|
+
ctx.stroke();
|
|
224
|
+
}
|
|
225
|
+
} else {
|
|
226
|
+
ctx.strokeStyle = colorString + ', 0.25)';
|
|
227
|
+
ctx.lineWidth = 1;
|
|
228
|
+
ctx.beginPath();
|
|
229
|
+
ctx.arc(cx, cy, currentRadius + 30, 0, Math.PI * 2);
|
|
230
|
+
ctx.stroke();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function updateCurrentColor() {
|
|
235
|
+
currentColor.r += (targetColor.r - currentColor.r) * 0.08;
|
|
236
|
+
currentColor.g += (targetColor.g - currentColor.g) * 0.08;
|
|
237
|
+
currentColor.b += (targetColor.b - currentColor.b) * 0.08;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function animate() {
|
|
241
|
+
if (!canvas) return;
|
|
242
|
+
const ctx = canvas.getContext('2d');
|
|
243
|
+
if (!ctx) return;
|
|
244
|
+
|
|
245
|
+
const dpr = window.devicePixelRatio || 1;
|
|
246
|
+
canvas.width = canvas.clientWidth * dpr;
|
|
247
|
+
canvas.height = canvas.clientHeight * dpr;
|
|
248
|
+
ctx.scale(dpr, dpr);
|
|
249
|
+
|
|
250
|
+
const w = canvas.width / dpr;
|
|
251
|
+
const h = canvas.height / dpr;
|
|
252
|
+
|
|
253
|
+
ctx.clearRect(0, 0, w, h);
|
|
254
|
+
|
|
255
|
+
currentRadius += (targetRadius - currentRadius) * 0.08;
|
|
256
|
+
currentSpeed += (targetSpeed - currentSpeed) * 0.08;
|
|
257
|
+
updateCurrentColor();
|
|
258
|
+
|
|
259
|
+
phase += currentSpeed;
|
|
260
|
+
|
|
261
|
+
const cx = w / 2;
|
|
262
|
+
const cy = h / 2;
|
|
263
|
+
|
|
264
|
+
const colorString = `rgba(${Math.floor(currentColor.r)}, ${Math.floor(currentColor.g)}, ${Math.floor(currentColor.b)}`;
|
|
265
|
+
|
|
266
|
+
if (root) {
|
|
267
|
+
root.style.boxShadow = `0 20px 40px rgba(${currentColor.r}, ${currentColor.g}, ${currentColor.b}, 0.06)`;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
renderParticles(ctx, cx, cy, colorString);
|
|
271
|
+
renderAtmosphereCircles(ctx, cx, cy, colorString);
|
|
272
|
+
|
|
273
|
+
ctx.beginPath();
|
|
274
|
+
ctx.strokeStyle = colorString + ', 0.8)';
|
|
275
|
+
ctx.lineWidth = 2.5;
|
|
276
|
+
ctx.arc(cx, cy, currentRadius, 0, Math.PI * 2);
|
|
277
|
+
ctx.stroke();
|
|
278
|
+
|
|
279
|
+
requestAnimationFrame(animate);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
selectEpoch(4);
|
|
283
|
+
animate();
|
|
284
|
+
|
|
285
|
+
window.addEventListener('resize', () => {
|
|
286
|
+
selectEpoch(currentEraIndex);
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
</script>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ScienceToolEntry } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const temperatureTimeline: ScienceToolEntry = {
|
|
4
|
+
id: 'planet-temperature-timeline',
|
|
5
|
+
icons: {
|
|
6
|
+
bg: 'mdi:weather-sunny',
|
|
7
|
+
fg: 'mdi:clock-outline',
|
|
8
|
+
},
|
|
9
|
+
i18n: {
|
|
10
|
+
es: () => import('./i18n/es').then((m) => m.content),
|
|
11
|
+
en: () => import('./i18n/en').then((m) => m.content),
|
|
12
|
+
fr: () => import('./i18n/fr').then((m) => m.content),
|
|
13
|
+
de: () => import('./i18n/de').then((m) => m.content),
|
|
14
|
+
it: () => import('./i18n/it').then((m) => m.content),
|
|
15
|
+
pt: () => import('./i18n/pt').then((m) => m.content),
|
|
16
|
+
id: () => import('./i18n/id').then((m) => m.content),
|
|
17
|
+
ja: () => import('./i18n/ja').then((m) => m.content),
|
|
18
|
+
ko: () => import('./i18n/ko').then((m) => m.content),
|
|
19
|
+
nl: () => import('./i18n/nl').then((m) => m.content),
|
|
20
|
+
pl: () => import('./i18n/pl').then((m) => m.content),
|
|
21
|
+
ru: () => import('./i18n/ru').then((m) => m.content),
|
|
22
|
+
sv: () => import('./i18n/sv').then((m) => m.content),
|
|
23
|
+
tr: () => import('./i18n/tr').then((m) => m.content),
|
|
24
|
+
zh: () => import('./i18n/zh').then((m) => m.content),
|
|
25
|
+
},
|
|
26
|
+
};
|