@driftengine/chemistry 4.7.0 → 4.7.1
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/README.md +1 -1
- package/dist/field/atmosphere.d.ts +5 -1
- package/dist/field/atmosphere.js +16 -2
- package/dist/parcel/store.d.ts +8 -0
- package/dist/parcel/store.js +12 -0
- package/package.json +1 -1
- package/src/field/atmosphere.ts +19 -2
- package/src/parcel/store.ts +13 -0
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ wants matter and no renderer pays for matter and no renderer.
|
|
|
31
31
|
| **The parcels meet the air** | A burning parcel draws the oxygen its reactions want out of the cell it sits in and pushes the gas they make into it. One relaxation, both directions |
|
|
32
32
|
| **Ignition is a criterion** | Five conditions, and `ignite` supplies one of them. **Holding a match to wet wood does nothing**, which is the API telling the truth about what a match is |
|
|
33
33
|
| **Extinction is four** | Fuel gone, oxygen below the limiting index, surface cooled, or blown off — and a smoulder survives air a flame cannot, which is why smothering leaves embers |
|
|
34
|
-
| **An event buffer** | Twenty-one kinds
|
|
34
|
+
| **An event buffer** | Twenty-one kinds declared and drained per tick, in the shape `drift/physics`'s contact buffer established. Four are emitted today: ignition, extinction, and a smoulder starting and ending. The other seventeen, melting and boiling among them, are named and not yet raised, so a consumer watching for one reads the parcel's phase or temperature instead |
|
|
35
35
|
| **Char glows for an hour** | Above about 800 K the chemistry outruns oxygen delivery, so the rate is transport, not kinetics. The two combine as a series resistance — and **blowing on an ember brightens it** |
|
|
36
36
|
| **A smoulder's smoke is pale** | Tar leaving a surface cracks to soot and burns where it is hot, and **condenses to droplets where it is not** — a 523 K dew point, which is Clausius-Clapeyron at the loading a plume carries rather than levoglucosan's 658 K boiling point. Two rates that climb with temperature against one gate that closes with it, so a flame blackens its own smoke and a smoulder whitens its own with nothing choosing. `aerosolAt` sums every carried species in the **liquid phase**, so the field never names a species this library invented |
|
|
37
37
|
| **A log dries, smokes and catches** | The whole of `§21`, asserted: the surface pins at the boiling point while the core stays cold, gas comes off, and it lights. **A green log at the same fire does not** |
|
|
@@ -214,12 +214,16 @@ export declare class AtmosphereField {
|
|
|
214
214
|
* **The quantity `visibilityAt` is derived from**, and the one a consumer attenuating a light
|
|
215
215
|
* along a ray actually needs: metres of visibility is one answer to it and `exp(−σ·d)` is another.
|
|
216
216
|
* Exporting only the first would have every consumer inverting Koschmieder to get back here.
|
|
217
|
+
*
|
|
218
|
+
* **Soot and droplets both.** It counted soot alone, so a smoulder's smoke, which is droplets,
|
|
219
|
+
* hid nothing and was drawn at an alpha of about 1e-29 by `emitSmoke`: the pale half of smoke
|
|
220
|
+
* was in the field and nowhere on screen.
|
|
217
221
|
*/
|
|
218
222
|
smokeDensityAt(x: number, y: number, z: number): number;
|
|
219
223
|
/**
|
|
220
224
|
* How far you can see, metres.
|
|
221
225
|
*
|
|
222
|
-
* Koschmieder over the
|
|
226
|
+
* Koschmieder over the smoke's extinction, soot and droplets. Clamped at ten kilometres, past which the answer stops
|
|
223
227
|
* being about the smoke.
|
|
224
228
|
*/
|
|
225
229
|
visibilityAt(x: number, y: number, z: number): number;
|
package/dist/field/atmosphere.js
CHANGED
|
@@ -36,6 +36,15 @@ export const CHUNK_SIZE = 8;
|
|
|
36
36
|
* rather than absorbs and runs lower.
|
|
37
37
|
*/
|
|
38
38
|
const SOOT_EXTINCTION = 8700;
|
|
39
|
+
/**
|
|
40
|
+
* Extinction per kilogram of droplets per cubic metre, m²/kg.
|
|
41
|
+
*
|
|
42
|
+
* The mass-specific extinction measured for smouldering-fire smoke, which is mostly liquid aerosol
|
|
43
|
+
* and scatters where soot absorbs: about half of flaming smoke's, per kilogram. One number for every
|
|
44
|
+
* droplet, which is right for tar condensing out of a smoulder, the aerosol this field was built
|
|
45
|
+
* for, and generous for coarse water droplets, which scatter less per kilogram.
|
|
46
|
+
*/
|
|
47
|
+
const AEROSOL_EXTINCTION = 4400;
|
|
39
48
|
/** Koschmieder's constant: the contrast threshold that defines "can no longer see it". */
|
|
40
49
|
const VISIBILITY_CONSTANT = 3.912;
|
|
41
50
|
/**
|
|
@@ -583,14 +592,19 @@ export class AtmosphereField {
|
|
|
583
592
|
* **The quantity `visibilityAt` is derived from**, and the one a consumer attenuating a light
|
|
584
593
|
* along a ray actually needs: metres of visibility is one answer to it and `exp(−σ·d)` is another.
|
|
585
594
|
* Exporting only the first would have every consumer inverting Koschmieder to get back here.
|
|
595
|
+
*
|
|
596
|
+
* **Soot and droplets both.** It counted soot alone, so a smoulder's smoke, which is droplets,
|
|
597
|
+
* hid nothing and was drawn at an alpha of about 1e-29 by `emitSmoke`: the pale half of smoke
|
|
598
|
+
* was in the field and nowhere on screen.
|
|
586
599
|
*/
|
|
587
600
|
smokeDensityAt(x, y, z) {
|
|
588
|
-
return (this.sootAt(x, y, z)
|
|
601
|
+
return ((this.sootAt(x, y, z) * SOOT_EXTINCTION + this.aerosolAt(x, y, z) * AEROSOL_EXTINCTION) /
|
|
602
|
+
this.cellVolume);
|
|
589
603
|
}
|
|
590
604
|
/**
|
|
591
605
|
* How far you can see, metres.
|
|
592
606
|
*
|
|
593
|
-
* Koschmieder over the
|
|
607
|
+
* Koschmieder over the smoke's extinction, soot and droplets. Clamped at ten kilometres, past which the answer stops
|
|
594
608
|
* being about the smoke.
|
|
595
609
|
*/
|
|
596
610
|
visibilityAt(x, y, z) {
|
package/dist/parcel/store.d.ts
CHANGED
|
@@ -294,6 +294,14 @@ export declare class ParcelStore {
|
|
|
294
294
|
* simultaneously consuming, which is what makes drying visible.
|
|
295
295
|
*/
|
|
296
296
|
wetnessOf(parcel: number): number;
|
|
297
|
+
/**
|
|
298
|
+
* Whether this parcel's substance can hold liquid water, which is what `wet` asks of it.
|
|
299
|
+
*
|
|
300
|
+
* A caller pouring rain over everything asks first: `wet` refuses a material with no water in its
|
|
301
|
+
* model, and refusing is right, but a loop over every parcel in the rain had no way to tell an
|
|
302
|
+
* iron nail from a log before it was told.
|
|
303
|
+
*/
|
|
304
|
+
wettable(parcel: number): boolean;
|
|
297
305
|
/**
|
|
298
306
|
* Pour water on it: liquid water into the surface shell, where the boiling reaction will find it.
|
|
299
307
|
*
|
package/dist/parcel/store.js
CHANGED
|
@@ -812,6 +812,18 @@ export class ParcelStore {
|
|
|
812
812
|
const free = held - authored;
|
|
813
813
|
return free > 0 ? free / area : 0;
|
|
814
814
|
}
|
|
815
|
+
/**
|
|
816
|
+
* Whether this parcel's substance can hold liquid water, which is what `wet` asks of it.
|
|
817
|
+
*
|
|
818
|
+
* A caller pouring rain over everything asks first: `wet` refuses a material with no water in its
|
|
819
|
+
* model, and refusing is right, but a loop over every parcel in the rain had no way to tell an
|
|
820
|
+
* iron nail from a log before it was told.
|
|
821
|
+
*/
|
|
822
|
+
wettable(parcel) {
|
|
823
|
+
const water = this.species.indexOf('H2O(l)');
|
|
824
|
+
const substance = this.substance[this.check(parcel)];
|
|
825
|
+
return water >= 0 && this.substances.localSlotOf(substance, water) >= 0;
|
|
826
|
+
}
|
|
815
827
|
/**
|
|
816
828
|
* Pour water on it: liquid water into the surface shell, where the boiling reaction will find it.
|
|
817
829
|
*
|
package/package.json
CHANGED
package/src/field/atmosphere.ts
CHANGED
|
@@ -49,6 +49,16 @@ export const CHUNK_SIZE = 8;
|
|
|
49
49
|
*/
|
|
50
50
|
const SOOT_EXTINCTION = 8700;
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Extinction per kilogram of droplets per cubic metre, m²/kg.
|
|
54
|
+
*
|
|
55
|
+
* The mass-specific extinction measured for smouldering-fire smoke, which is mostly liquid aerosol
|
|
56
|
+
* and scatters where soot absorbs: about half of flaming smoke's, per kilogram. One number for every
|
|
57
|
+
* droplet, which is right for tar condensing out of a smoulder, the aerosol this field was built
|
|
58
|
+
* for, and generous for coarse water droplets, which scatter less per kilogram.
|
|
59
|
+
*/
|
|
60
|
+
const AEROSOL_EXTINCTION = 4400;
|
|
61
|
+
|
|
52
62
|
/** Koschmieder's constant: the contrast threshold that defines "can no longer see it". */
|
|
53
63
|
const VISIBILITY_CONSTANT = 3.912;
|
|
54
64
|
|
|
@@ -657,15 +667,22 @@ export class AtmosphereField {
|
|
|
657
667
|
* **The quantity `visibilityAt` is derived from**, and the one a consumer attenuating a light
|
|
658
668
|
* along a ray actually needs: metres of visibility is one answer to it and `exp(−σ·d)` is another.
|
|
659
669
|
* Exporting only the first would have every consumer inverting Koschmieder to get back here.
|
|
670
|
+
*
|
|
671
|
+
* **Soot and droplets both.** It counted soot alone, so a smoulder's smoke, which is droplets,
|
|
672
|
+
* hid nothing and was drawn at an alpha of about 1e-29 by `emitSmoke`: the pale half of smoke
|
|
673
|
+
* was in the field and nowhere on screen.
|
|
660
674
|
*/
|
|
661
675
|
smokeDensityAt(x: number, y: number, z: number): number {
|
|
662
|
-
return (
|
|
676
|
+
return (
|
|
677
|
+
(this.sootAt(x, y, z) * SOOT_EXTINCTION + this.aerosolAt(x, y, z) * AEROSOL_EXTINCTION) /
|
|
678
|
+
this.cellVolume
|
|
679
|
+
);
|
|
663
680
|
}
|
|
664
681
|
|
|
665
682
|
/**
|
|
666
683
|
* How far you can see, metres.
|
|
667
684
|
*
|
|
668
|
-
* Koschmieder over the
|
|
685
|
+
* Koschmieder over the smoke's extinction, soot and droplets. Clamped at ten kilometres, past which the answer stops
|
|
669
686
|
* being about the smoke.
|
|
670
687
|
*/
|
|
671
688
|
visibilityAt(x: number, y: number, z: number): number {
|
package/src/parcel/store.ts
CHANGED
|
@@ -915,6 +915,19 @@ export class ParcelStore {
|
|
|
915
915
|
return free > 0 ? free / area : 0;
|
|
916
916
|
}
|
|
917
917
|
|
|
918
|
+
/**
|
|
919
|
+
* Whether this parcel's substance can hold liquid water, which is what `wet` asks of it.
|
|
920
|
+
*
|
|
921
|
+
* A caller pouring rain over everything asks first: `wet` refuses a material with no water in its
|
|
922
|
+
* model, and refusing is right, but a loop over every parcel in the rain had no way to tell an
|
|
923
|
+
* iron nail from a log before it was told.
|
|
924
|
+
*/
|
|
925
|
+
wettable(parcel: number): boolean {
|
|
926
|
+
const water = this.species.indexOf('H2O(l)');
|
|
927
|
+
const substance = this.substance[this.check(parcel)] as number;
|
|
928
|
+
return water >= 0 && this.substances.localSlotOf(substance, water) >= 0;
|
|
929
|
+
}
|
|
930
|
+
|
|
918
931
|
/**
|
|
919
932
|
* Pour water on it: liquid water into the surface shell, where the boiling reaction will find it.
|
|
920
933
|
*
|