@a2f0/skyline 0.2.14 → 0.2.16

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.
@@ -1,11 +1,14 @@
1
+ import * as THREE from "../vendor/three-r186.js";
2
+ import { chainSkin, chainsOf } from "./facade-grid.js";
1
3
  import { createBuilder, inside, line, polygonOf } from "./building-kit.js";
2
4
  // Kemper Building at 1 East Wacker Drive: the geographic layout's detailed
3
5
  // model. The mapped ground outline and tower part keep their OpenStreetMap
4
6
  // outlines; the 159 m top is OSM's height tag (522 ft in the 1962 record).
5
7
  // The marble shell, window bays, raised mullions, dark crown band with light
6
8
  // fins, and projecting cap reuse the fitted model's vocabulary at meter scale.
7
- // Window-column and pane proportions follow a 2012 photograph; bay counts,
8
- // window-row and crown spacings remain estimates. See
9
+ // Window columns follow dated photographs: twelve north/south, fourteen
10
+ // east/west, with a shallow top-floor band. Row pitch and crown pane counts
11
+ // remain estimates. See
9
12
  // docs/kemper-geographic-reference.md. Units are meters; +x is east, +z south.
10
13
  export const kemperGeographicLevels = Object.freeze({
11
14
  podium: 7.8, // OSM way 685494067: two floors at the tower's average floor height
@@ -16,10 +19,11 @@ const h = kemperGeographicLevels;
16
19
  // Floors 1-2 are the podium; the tower pitch follows the published 41 floors.
17
20
  const pitch = (h.roof - h.podium) / (h.floors - 2);
18
21
  const floorHeight = (floor) => h.podium + (floor - 3) * pitch;
19
- // The crown band spans the 40th and 41st floors up to the roof: dark glazing
20
- // behind light fins, capped by a projecting parapet.
21
- const crownBase = floorHeight(40);
22
+ // FID-KEM-008: the 2012 north elevation shows a shallow top-floor band,
23
+ // about 0.6–0.7 of an ordinary floor repeat, not two full floors. The 0.7
24
+ // assembly includes the small clearances around the glass below the cap.
22
25
  const shaftTop = h.roof - 0.5;
26
+ const crownBase = shaftTop - 0.7 * pitch;
23
27
  export function createKemperGeographicBuilding(record, projectPlan, offset) {
24
28
  const kit = createBuilder(record.name, record.id);
25
29
  // The neutral gray palette of the fitted model: marble shell, dark glazing
@@ -63,26 +67,46 @@ export function createKemperGeographicBuilding(record, projectPlan, offset) {
63
67
  // FID-KEM-002: the 2012 north-face photograph gives openings about 48% of
64
68
  // each bay and glass about 52% of each floor pitch. Opaque panels continue
65
69
  // the narrow columns between windows; the marble piers stay uninterrupted.
66
- tower.forEach((run, side) => {
67
- const bays = Math.max(1, Math.round(run.length / 3.1));
68
- const width = run.length / bays;
70
+ // Keep one rhythm across the map's almost-collinear tracing vertices. The
71
+ // 15° tolerance includes the short east-wall jog, but not the four corners.
72
+ const faces = chainsOf(tower, Math.PI / 12);
73
+ if (faces.length !== 4)
74
+ throw new Error("Kemper's tower must have four facade chains.");
75
+ const white = new THREE.Color(0xffffff);
76
+ const faceStrip = (batch, face, from, to, y0, y1, back, front) => {
77
+ const clearance = Math.max(Math.abs(back), Math.abs(front)) + 0.025;
78
+ from = Math.max(from, clearance);
79
+ to = Math.min(to, face.length - clearance);
80
+ if (to - from < 0.03 || y1 <= y0)
81
+ return;
82
+ const first = face.starts.findLastIndex((s) => s <= from);
83
+ const last = face.starts.findLastIndex((s) => s < to);
84
+ // A single closed skin mitres internal joints; neither a new bay nor a
85
+ // visible gap is introduced where the OSM tracing splits a physical face.
86
+ chainSkin(kit, batch, tower, face, first, last, from, to, [], [y0, y1], back, front, () => white, white);
87
+ };
88
+ faces.forEach((face, side) => {
89
+ // Twelve north columns in 2012; fourteen west columns in 2016. Matching
90
+ // counts on the opposite faces are an explicit symmetry inference.
91
+ const normal = tower[face.runs[0]].normal(0);
92
+ const bays = Math.abs(normal[0]) > Math.abs(normal[1]) ? 14 : 12;
93
+ const width = face.length / bays;
69
94
  const inset = width * 0.26;
70
95
  for (let bay = 0; bay < bays; bay += 1) {
71
- strip(spandrels, run, bay * width + inset, (bay + 1) * width - inset, h.podium, crownBase, 0.01, 0.04);
96
+ faceStrip(spandrels, face, bay * width + inset, (bay + 1) * width - inset, h.podium, crownBase, 0.01, 0.04);
72
97
  }
73
- for (let floor = 3; floor <= 39; floor += 1) {
98
+ for (let floor = 3; floor <= 40; floor += 1) {
74
99
  const bottom = floorHeight(floor), top = floorHeight(floor + 1);
75
100
  for (let bay = 0; bay < bays; bay += 1) {
76
- strip(paneTone(floor, bay, side), run, bay * width + inset + 0.01, (bay + 1) * width - inset - 0.01, bottom + pitch * 0.24, top - pitch * 0.24, 0.02, 0.07);
101
+ faceStrip(paneTone(floor, bay, side), face, bay * width + inset + 0.01, (bay + 1) * width - inset - 0.01, bottom + pitch * 0.24, top - pitch * 0.24, 0.02, 0.07);
77
102
  }
78
103
  }
79
104
  for (let i = 0; i <= bays; i += 1) {
80
- const s = Math.max(0.18, Math.min(run.length - 0.18, i * width));
81
- strip(mullions, run, s - inset, s + inset, h.podium + 0.02, crownBase - 0.05, 0.05, 0.16);
105
+ faceStrip(mullions, face, i * width - inset, i * width + inset, h.podium + 0.02, crownBase - 0.05, 0.05, 0.16);
82
106
  }
83
- strip(mullions, run, 0.015, run.length - 0.015, h.podium - 0.05, h.podium + 0.35, 0.06, 0.18);
107
+ faceStrip(mullions, face, 0.015, face.length - 0.015, h.podium - 0.05, h.podium + 0.35, 0.06, 0.18);
84
108
  });
85
- // The crown: dark glazing between light fins over the top two floors, then
109
+ // The crown: a shallow top-floor glazing band with slender frames, then
86
110
  // a projecting closed cap at the 159 m roof. The mapped outline carries
87
111
  // sub-meter tracing jogs on its north and west edges; the prisms keep them,
88
112
  // but a closed band cannot turn their concave corners, so the cap ring drops
@@ -95,7 +119,7 @@ export function createKemperGeographicBuilding(record, projectPlan, offset) {
95
119
  const count = Math.max(1, Math.round(run.length / 1.05));
96
120
  for (let i = 0; i < count; i += 1) {
97
121
  const s = (i + 0.5) * run.length / count;
98
- strip(crownFins, run, s - 0.13, s + 0.13, crownBase - 0.05, shaftTop - 0.2, 0.04, 0.28);
122
+ strip(crownFins, run, s - 0.05, s + 0.05, crownBase - 0.05, shaftTop - 0.2, 0.04, 0.28);
99
123
  }
100
124
  });
101
125
  const simplified = tower.map((run) => run.at(0));
@@ -13,7 +13,7 @@
13
13
  // Coordinates are [longitude, latitude] in WGS84. See docs/skyline-geography.md.
14
14
  export const geographicBuildings = [
15
15
  { "id": "building-heritage-at-millennium-park", "name": "The Heritage at Millennium Park", "shortName": "Heritage", "height": 192.4, "tipHeight": 192.4, "heightSource": "https://www.skyscrapercenter.com/building/the-heritage-at-millennium-park/1897", "note": "Detailed Heritage: mapped footprint; 192.4 m published top and 176.8 m occupied floor. Podium 32.8 m and lower terrace 89.5 m from city design drawings; facade spacing and roof equipment estimated.", "footprint": { "way": 147397547, "version": 7, "coordinates": [[-87.6260719, 41.8843928], [-87.6257481, 41.8843917], [-87.6257191, 41.8843891], [-87.6256478, 41.8843865], [-87.6255526, 41.884377], [-87.6254712, 41.8843689], [-87.6254652, 41.8841954], [-87.6254621, 41.8841073], [-87.6254542, 41.883665], [-87.6254571, 41.8836595], [-87.6254538, 41.8836092], [-87.6260475, 41.8836083]] }, "parts": [{ "way": 686199648, "version": 4, "coordinates": [[-87.6256927, 41.8839116], [-87.6256884, 41.883961], [-87.6255146, 41.8839622], [-87.6254791, 41.8840238], [-87.6254621, 41.8841073], [-87.6254652, 41.8841954], [-87.6254854, 41.8842648], [-87.6255135, 41.8842688], [-87.6255389, 41.8843317], [-87.6255398, 41.8843608], [-87.6257484, 41.8843597], [-87.6257491, 41.8842951], [-87.6258081, 41.8842912], [-87.6258044, 41.8842312], [-87.6258273, 41.8842124], [-87.6258393, 41.8841657], [-87.6258472, 41.8841049], [-87.6258401, 41.8840613], [-87.6258255, 41.8840069], [-87.6257984, 41.8839707], [-87.6257899, 41.883913], [-87.6257556, 41.8839132]], "bottom": 0, "top": 192.4 }, { "way": 686199649, "version": 1, "coordinates": [[-87.6257591, 41.8837593], [-87.6257556, 41.8839132], [-87.6256927, 41.8839116], [-87.6256884, 41.883961], [-87.6255146, 41.8839622], [-87.6254791, 41.8840238], [-87.6254621, 41.8841073], [-87.6254542, 41.883665], [-87.6257265, 41.8836609], [-87.6257315, 41.8837592]], "bottom": 0, "top": 89.5096, "estimatedHeight": true }, { "way": 686199650, "version": 2, "coordinates": [[-87.6254542, 41.883665], [-87.6254571, 41.8836595], [-87.6254538, 41.8836092], [-87.6260475, 41.8836083], [-87.6260719, 41.8843928], [-87.6257481, 41.8843917], [-87.6257484, 41.8843597], [-87.6257491, 41.8842951], [-87.6258081, 41.8842912], [-87.6258044, 41.8842312], [-87.6258273, 41.8842124], [-87.6258393, 41.8841657], [-87.6258472, 41.8841049], [-87.6258401, 41.8840613], [-87.6258255, 41.8840069], [-87.6257984, 41.8839707], [-87.6257899, 41.883913], [-87.6257556, 41.8839132], [-87.6257591, 41.8837593], [-87.6257315, 41.8837592], [-87.6257265, 41.8836609]], "bottom": 0, "top": 32.8176, "estimatedHeight": true }] },
16
- { "id": "building-kemper", "name": "Kemper Building", "shortName": "Kemper", "height": 159, "tipHeight": 159, "heightSource": "https://www.openstreetmap.org/way/64389514", "note": "Detailed Kemper: mapped footprint, tower part, and 159 m OpenStreetMap height; 41 published floors. Window rows, bays, and the finned crown are estimated.", "footprint": { "way": 64389514, "version": 13, "coordinates": [[-87.6277499, 41.8863375], [-87.6273407, 41.8863436], [-87.6273396, 41.8863339], [-87.6271328, 41.8863379], [-87.6270968, 41.8863386], [-87.6270991, 41.8866309], [-87.6271288, 41.8866306], [-87.6272242, 41.8866295], [-87.6273446, 41.8866282], [-87.627346, 41.8866984], [-87.6273463, 41.8867111], [-87.6273478, 41.8867197], [-87.627381, 41.8867192], [-87.6275197, 41.8867167], [-87.6277622, 41.8867154], [-87.6277552, 41.8865163]] }, "parts": [{ "way": 685494066, "version": 4, "coordinates": [[-87.6273446, 41.8866282], [-87.627346, 41.8866984], [-87.6273463, 41.8867111], [-87.6273478, 41.8867197], [-87.627381, 41.8867192], [-87.6275197, 41.8867167], [-87.6277622, 41.8867154], [-87.6277552, 41.8865163], [-87.6277499, 41.8863375], [-87.6273407, 41.8863436]], "bottom": 0, "top": 159 }, { "way": 685494067, "version": 4, "coordinates": [[-87.6273407, 41.8863436], [-87.6273446, 41.8866282], [-87.6272242, 41.8866295], [-87.6271288, 41.8866306], [-87.6270991, 41.8866309], [-87.6270968, 41.8863386], [-87.6271328, 41.8863379], [-87.6273396, 41.8863339]], "bottom": 0, "top": 7.8, "estimatedHeight": true }] },
16
+ { "id": "building-kemper", "name": "Kemper Building", "shortName": "Kemper", "height": 159, "tipHeight": 159, "heightSource": "https://www.openstreetmap.org/way/64389514", "note": "Detailed Kemper: mapped footprint, tower part, and 159 m OpenStreetMap height; 41 published floors. Photographed 12/14-column facade rhythm and shallow crown; row pitch and crown pane counts remain estimated.", "footprint": { "way": 64389514, "version": 13, "coordinates": [[-87.6277499, 41.8863375], [-87.6273407, 41.8863436], [-87.6273396, 41.8863339], [-87.6271328, 41.8863379], [-87.6270968, 41.8863386], [-87.6270991, 41.8866309], [-87.6271288, 41.8866306], [-87.6272242, 41.8866295], [-87.6273446, 41.8866282], [-87.627346, 41.8866984], [-87.6273463, 41.8867111], [-87.6273478, 41.8867197], [-87.627381, 41.8867192], [-87.6275197, 41.8867167], [-87.6277622, 41.8867154], [-87.6277552, 41.8865163]] }, "parts": [{ "way": 685494066, "version": 4, "coordinates": [[-87.6273446, 41.8866282], [-87.627346, 41.8866984], [-87.6273463, 41.8867111], [-87.6273478, 41.8867197], [-87.627381, 41.8867192], [-87.6275197, 41.8867167], [-87.6277622, 41.8867154], [-87.6277552, 41.8865163], [-87.6277499, 41.8863375], [-87.6273407, 41.8863436]], "bottom": 0, "top": 159 }, { "way": 685494067, "version": 4, "coordinates": [[-87.6273407, 41.8863436], [-87.6273446, 41.8866282], [-87.6272242, 41.8866295], [-87.6271288, 41.8866306], [-87.6270991, 41.8866309], [-87.6270968, 41.8863386], [-87.6271328, 41.8863379], [-87.6273396, 41.8863339]], "bottom": 0, "top": 7.8, "estimatedHeight": true }] },
17
17
  { "id": "building-crain-communications", "name": "Crain Communications Building", "shortName": "Crain", "height": 177.4, "tipHeight": 177.4, "heightSource": "https://www.skyscrapercenter.com/building/150-north-michigan-avenue/2441", "note": "Detailed Crain: mapped parts, split, notches and roof bearing; roof slope and both peaks at the published 177.4 m measured on the source photograph, with its 3.5 m band pitch. Lobby, window module and roof grid estimated.", "footprint": { "way": 210671717, "version": 14, "coordinates": [[-87.6251984, 41.8846185], [-87.6252086, 41.8849637], [-87.6251849, 41.884981], [-87.6251613, 41.8849983], [-87.6252038, 41.8850292], [-87.6247448, 41.8850344], [-87.624744, 41.8850059], [-87.6247427, 41.8849565], [-87.6247379, 41.8847857], [-87.6247375, 41.8847566], [-87.6248159, 41.8846976], [-87.6248301, 41.884687], [-87.6248227, 41.8846815], [-87.6247345, 41.8846233], [-87.624814, 41.8846217], [-87.624879, 41.8846221]] }, "parts": [{ "way": 284816227, "version": 2, "coordinates": [[-87.6250471, 41.8849145], [-87.625093, 41.8848805], [-87.6252086, 41.8849637], [-87.6251849, 41.884981], [-87.6251613, 41.8849983]], "bottom": 0, "top": 152.5 }, { "way": 284816228, "version": 2, "coordinates": [[-87.6247345, 41.8846233], [-87.6248227, 41.8846815], [-87.6248301, 41.884687], [-87.625093, 41.8848805], [-87.6252086, 41.8849637], [-87.6251984, 41.8846185], [-87.624879, 41.8846221], [-87.624814, 41.8846217]], "bottom": 0, "top": 172.4, "roofSlope": { "direction": 133.0, "height": 75.0 } }, { "way": 284816229, "version": 2, "coordinates": [[-87.6251613, 41.8849983], [-87.6250471, 41.8849145], [-87.625093, 41.8848805], [-87.6248301, 41.884687], [-87.6248159, 41.8846976], [-87.6247375, 41.8847566], [-87.6247379, 41.8847857], [-87.6247427, 41.8849565], [-87.624744, 41.8850059], [-87.6247448, 41.8850344], [-87.6252038, 41.8850292]], "bottom": 0, "top": 177.4, "roofSlope": { "direction": 133.0, "height": 73.0 } }] },
18
18
  { "id": "building-330-north-wabash", "name": "330 North Wabash", "shortName": "330 N Wabash", "height": 211.84, "tipHeight": 211.84, "heightSource": "https://en.wikipedia.org/wiki/330_North_Wabash", "note": "Detailed 330 North Wabash (AMA Plaza): the tower the drawing labels Michigan Plaza South, whose roof corners land on the drawn ones through the photograph's camera. Mapped outline and 211.84 m (695 ft) height; bronze I-beam mullions on the 5 ft module, bronze spandrels and glass on 12 ft 6 in floors, a lobby on the 25 ft plaza, and louvered mechanical floors. No drawn model; the original layout has no counterpart.", "footprint": { "way": 64596068, "version": 20, "coordinates": [[-87.6278354, 41.8890249], [-87.627387, 41.8890335], [-87.6273775, 41.888809], [-87.6273647, 41.8885094], [-87.6273551, 41.8882848], [-87.6278057, 41.8882763]] }, "parts": [{ "way": 64596068, "version": 20, "coordinates": [[-87.6278354, 41.8890249], [-87.627387, 41.8890335], [-87.6273775, 41.888809], [-87.6273647, 41.8885094], [-87.6273551, 41.8882848], [-87.6278057, 41.8882763]], "bottom": 0, "top": 211.84 }] },
19
19
  { "id": "building-michigan-plaza-south-tower", "name": "Michigan Plaza South", "shortName": "Michigan Plaza S", "height": 168.6, "tipHeight": 168.6, "heightSource": "https://www.skyscrapercenter.com/building/michigan-plaza-south/2825", "note": "Detailed Michigan Plaza South: mapped outline, published 168.6 m height, and a 44-story glazed grid; window rows and bays estimated.", "footprint": { "way": 127107024, "version": 12, "coordinates": [[-87.6231713, 41.8858935], [-87.6239484, 41.8858842], [-87.6239505, 41.8861715], [-87.6238471, 41.8861735], [-87.6238486, 41.886216], [-87.6238504, 41.8862675], [-87.6235019, 41.8862724], [-87.6235019, 41.8861725], [-87.6231743, 41.8861722]] }, "parts": [{ "way": 127107024, "version": 12, "coordinates": [[-87.6231713, 41.8858935], [-87.6239484, 41.8858842], [-87.6239505, 41.8861715], [-87.6238471, 41.8861735], [-87.6238486, 41.886216], [-87.6238504, 41.8862675], [-87.6235019, 41.8862724], [-87.6235019, 41.8861725], [-87.6231743, 41.8861722]], "bottom": 0, "top": 168.6 }] },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a2f0/skyline",
3
- "version": "0.2.14",
3
+ "version": "0.2.16",
4
4
  "description": "Interactive Chicago skyline viewer and reusable Three.js building models",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -1,11 +1,14 @@
1
+ import * as THREE from "../vendor/three-r186.js";
2
+ import { chainSkin, chainsOf } from "./facade-grid.js";
1
3
  import { createBuilder, inside, line, polygonOf } from "./building-kit.js";
2
4
  // Kemper Building at 1 East Wacker Drive: the geographic layout's detailed
3
5
  // model. The mapped ground outline and tower part keep their OpenStreetMap
4
6
  // outlines; the 159 m top is OSM's height tag (522 ft in the 1962 record).
5
7
  // The marble shell, window bays, raised mullions, dark crown band with light
6
8
  // fins, and projecting cap reuse the fitted model's vocabulary at meter scale.
7
- // Window-column and pane proportions follow a 2012 photograph; bay counts,
8
- // window-row and crown spacings remain estimates. See
9
+ // Window columns follow dated photographs: twelve north/south, fourteen
10
+ // east/west, with a shallow top-floor band. Row pitch and crown pane counts
11
+ // remain estimates. See
9
12
  // docs/kemper-geographic-reference.md. Units are meters; +x is east, +z south.
10
13
  export const kemperGeographicLevels = Object.freeze({
11
14
  podium: 7.8, // OSM way 685494067: two floors at the tower's average floor height
@@ -16,10 +19,11 @@ const h = kemperGeographicLevels;
16
19
  // Floors 1-2 are the podium; the tower pitch follows the published 41 floors.
17
20
  const pitch = (h.roof - h.podium) / (h.floors - 2);
18
21
  const floorHeight = (floor) => h.podium + (floor - 3) * pitch;
19
- // The crown band spans the 40th and 41st floors up to the roof: dark glazing
20
- // behind light fins, capped by a projecting parapet.
21
- const crownBase = floorHeight(40);
22
+ // FID-KEM-008: the 2012 north elevation shows a shallow top-floor band,
23
+ // about 0.6–0.7 of an ordinary floor repeat, not two full floors. The 0.7
24
+ // assembly includes the small clearances around the glass below the cap.
22
25
  const shaftTop = h.roof - 0.5;
26
+ const crownBase = shaftTop - 0.7 * pitch;
23
27
  export function createKemperGeographicBuilding(record, projectPlan, offset) {
24
28
  const kit = createBuilder(record.name, record.id);
25
29
  // The neutral gray palette of the fitted model: marble shell, dark glazing
@@ -63,26 +67,46 @@ export function createKemperGeographicBuilding(record, projectPlan, offset) {
63
67
  // FID-KEM-002: the 2012 north-face photograph gives openings about 48% of
64
68
  // each bay and glass about 52% of each floor pitch. Opaque panels continue
65
69
  // the narrow columns between windows; the marble piers stay uninterrupted.
66
- tower.forEach((run, side) => {
67
- const bays = Math.max(1, Math.round(run.length / 3.1));
68
- const width = run.length / bays;
70
+ // Keep one rhythm across the map's almost-collinear tracing vertices. The
71
+ // 15° tolerance includes the short east-wall jog, but not the four corners.
72
+ const faces = chainsOf(tower, Math.PI / 12);
73
+ if (faces.length !== 4)
74
+ throw new Error("Kemper's tower must have four facade chains.");
75
+ const white = new THREE.Color(0xffffff);
76
+ const faceStrip = (batch, face, from, to, y0, y1, back, front) => {
77
+ const clearance = Math.max(Math.abs(back), Math.abs(front)) + 0.025;
78
+ from = Math.max(from, clearance);
79
+ to = Math.min(to, face.length - clearance);
80
+ if (to - from < 0.03 || y1 <= y0)
81
+ return;
82
+ const first = face.starts.findLastIndex((s) => s <= from);
83
+ const last = face.starts.findLastIndex((s) => s < to);
84
+ // A single closed skin mitres internal joints; neither a new bay nor a
85
+ // visible gap is introduced where the OSM tracing splits a physical face.
86
+ chainSkin(kit, batch, tower, face, first, last, from, to, [], [y0, y1], back, front, () => white, white);
87
+ };
88
+ faces.forEach((face, side) => {
89
+ // Twelve north columns in 2012; fourteen west columns in 2016. Matching
90
+ // counts on the opposite faces are an explicit symmetry inference.
91
+ const normal = tower[face.runs[0]].normal(0);
92
+ const bays = Math.abs(normal[0]) > Math.abs(normal[1]) ? 14 : 12;
93
+ const width = face.length / bays;
69
94
  const inset = width * 0.26;
70
95
  for (let bay = 0; bay < bays; bay += 1) {
71
- strip(spandrels, run, bay * width + inset, (bay + 1) * width - inset, h.podium, crownBase, 0.01, 0.04);
96
+ faceStrip(spandrels, face, bay * width + inset, (bay + 1) * width - inset, h.podium, crownBase, 0.01, 0.04);
72
97
  }
73
- for (let floor = 3; floor <= 39; floor += 1) {
98
+ for (let floor = 3; floor <= 40; floor += 1) {
74
99
  const bottom = floorHeight(floor), top = floorHeight(floor + 1);
75
100
  for (let bay = 0; bay < bays; bay += 1) {
76
- strip(paneTone(floor, bay, side), run, bay * width + inset + 0.01, (bay + 1) * width - inset - 0.01, bottom + pitch * 0.24, top - pitch * 0.24, 0.02, 0.07);
101
+ faceStrip(paneTone(floor, bay, side), face, bay * width + inset + 0.01, (bay + 1) * width - inset - 0.01, bottom + pitch * 0.24, top - pitch * 0.24, 0.02, 0.07);
77
102
  }
78
103
  }
79
104
  for (let i = 0; i <= bays; i += 1) {
80
- const s = Math.max(0.18, Math.min(run.length - 0.18, i * width));
81
- strip(mullions, run, s - inset, s + inset, h.podium + 0.02, crownBase - 0.05, 0.05, 0.16);
105
+ faceStrip(mullions, face, i * width - inset, i * width + inset, h.podium + 0.02, crownBase - 0.05, 0.05, 0.16);
82
106
  }
83
- strip(mullions, run, 0.015, run.length - 0.015, h.podium - 0.05, h.podium + 0.35, 0.06, 0.18);
107
+ faceStrip(mullions, face, 0.015, face.length - 0.015, h.podium - 0.05, h.podium + 0.35, 0.06, 0.18);
84
108
  });
85
- // The crown: dark glazing between light fins over the top two floors, then
109
+ // The crown: a shallow top-floor glazing band with slender frames, then
86
110
  // a projecting closed cap at the 159 m roof. The mapped outline carries
87
111
  // sub-meter tracing jogs on its north and west edges; the prisms keep them,
88
112
  // but a closed band cannot turn their concave corners, so the cap ring drops
@@ -95,7 +119,7 @@ export function createKemperGeographicBuilding(record, projectPlan, offset) {
95
119
  const count = Math.max(1, Math.round(run.length / 1.05));
96
120
  for (let i = 0; i < count; i += 1) {
97
121
  const s = (i + 0.5) * run.length / count;
98
- strip(crownFins, run, s - 0.13, s + 0.13, crownBase - 0.05, shaftTop - 0.2, 0.04, 0.28);
122
+ strip(crownFins, run, s - 0.05, s + 0.05, crownBase - 0.05, shaftTop - 0.2, 0.04, 0.28);
99
123
  }
100
124
  });
101
125
  const simplified = tower.map((run) => run.at(0));
@@ -13,7 +13,7 @@
13
13
  // Coordinates are [longitude, latitude] in WGS84. See docs/skyline-geography.md.
14
14
  export const geographicBuildings = [
15
15
  { "id": "building-heritage-at-millennium-park", "name": "The Heritage at Millennium Park", "shortName": "Heritage", "height": 192.4, "tipHeight": 192.4, "heightSource": "https://www.skyscrapercenter.com/building/the-heritage-at-millennium-park/1897", "note": "Detailed Heritage: mapped footprint; 192.4 m published top and 176.8 m occupied floor. Podium 32.8 m and lower terrace 89.5 m from city design drawings; facade spacing and roof equipment estimated.", "footprint": { "way": 147397547, "version": 7, "coordinates": [[-87.6260719, 41.8843928], [-87.6257481, 41.8843917], [-87.6257191, 41.8843891], [-87.6256478, 41.8843865], [-87.6255526, 41.884377], [-87.6254712, 41.8843689], [-87.6254652, 41.8841954], [-87.6254621, 41.8841073], [-87.6254542, 41.883665], [-87.6254571, 41.8836595], [-87.6254538, 41.8836092], [-87.6260475, 41.8836083]] }, "parts": [{ "way": 686199648, "version": 4, "coordinates": [[-87.6256927, 41.8839116], [-87.6256884, 41.883961], [-87.6255146, 41.8839622], [-87.6254791, 41.8840238], [-87.6254621, 41.8841073], [-87.6254652, 41.8841954], [-87.6254854, 41.8842648], [-87.6255135, 41.8842688], [-87.6255389, 41.8843317], [-87.6255398, 41.8843608], [-87.6257484, 41.8843597], [-87.6257491, 41.8842951], [-87.6258081, 41.8842912], [-87.6258044, 41.8842312], [-87.6258273, 41.8842124], [-87.6258393, 41.8841657], [-87.6258472, 41.8841049], [-87.6258401, 41.8840613], [-87.6258255, 41.8840069], [-87.6257984, 41.8839707], [-87.6257899, 41.883913], [-87.6257556, 41.8839132]], "bottom": 0, "top": 192.4 }, { "way": 686199649, "version": 1, "coordinates": [[-87.6257591, 41.8837593], [-87.6257556, 41.8839132], [-87.6256927, 41.8839116], [-87.6256884, 41.883961], [-87.6255146, 41.8839622], [-87.6254791, 41.8840238], [-87.6254621, 41.8841073], [-87.6254542, 41.883665], [-87.6257265, 41.8836609], [-87.6257315, 41.8837592]], "bottom": 0, "top": 89.5096, "estimatedHeight": true }, { "way": 686199650, "version": 2, "coordinates": [[-87.6254542, 41.883665], [-87.6254571, 41.8836595], [-87.6254538, 41.8836092], [-87.6260475, 41.8836083], [-87.6260719, 41.8843928], [-87.6257481, 41.8843917], [-87.6257484, 41.8843597], [-87.6257491, 41.8842951], [-87.6258081, 41.8842912], [-87.6258044, 41.8842312], [-87.6258273, 41.8842124], [-87.6258393, 41.8841657], [-87.6258472, 41.8841049], [-87.6258401, 41.8840613], [-87.6258255, 41.8840069], [-87.6257984, 41.8839707], [-87.6257899, 41.883913], [-87.6257556, 41.8839132], [-87.6257591, 41.8837593], [-87.6257315, 41.8837592], [-87.6257265, 41.8836609]], "bottom": 0, "top": 32.8176, "estimatedHeight": true }] },
16
- { "id": "building-kemper", "name": "Kemper Building", "shortName": "Kemper", "height": 159, "tipHeight": 159, "heightSource": "https://www.openstreetmap.org/way/64389514", "note": "Detailed Kemper: mapped footprint, tower part, and 159 m OpenStreetMap height; 41 published floors. Window rows, bays, and the finned crown are estimated.", "footprint": { "way": 64389514, "version": 13, "coordinates": [[-87.6277499, 41.8863375], [-87.6273407, 41.8863436], [-87.6273396, 41.8863339], [-87.6271328, 41.8863379], [-87.6270968, 41.8863386], [-87.6270991, 41.8866309], [-87.6271288, 41.8866306], [-87.6272242, 41.8866295], [-87.6273446, 41.8866282], [-87.627346, 41.8866984], [-87.6273463, 41.8867111], [-87.6273478, 41.8867197], [-87.627381, 41.8867192], [-87.6275197, 41.8867167], [-87.6277622, 41.8867154], [-87.6277552, 41.8865163]] }, "parts": [{ "way": 685494066, "version": 4, "coordinates": [[-87.6273446, 41.8866282], [-87.627346, 41.8866984], [-87.6273463, 41.8867111], [-87.6273478, 41.8867197], [-87.627381, 41.8867192], [-87.6275197, 41.8867167], [-87.6277622, 41.8867154], [-87.6277552, 41.8865163], [-87.6277499, 41.8863375], [-87.6273407, 41.8863436]], "bottom": 0, "top": 159 }, { "way": 685494067, "version": 4, "coordinates": [[-87.6273407, 41.8863436], [-87.6273446, 41.8866282], [-87.6272242, 41.8866295], [-87.6271288, 41.8866306], [-87.6270991, 41.8866309], [-87.6270968, 41.8863386], [-87.6271328, 41.8863379], [-87.6273396, 41.8863339]], "bottom": 0, "top": 7.8, "estimatedHeight": true }] },
16
+ { "id": "building-kemper", "name": "Kemper Building", "shortName": "Kemper", "height": 159, "tipHeight": 159, "heightSource": "https://www.openstreetmap.org/way/64389514", "note": "Detailed Kemper: mapped footprint, tower part, and 159 m OpenStreetMap height; 41 published floors. Photographed 12/14-column facade rhythm and shallow crown; row pitch and crown pane counts remain estimated.", "footprint": { "way": 64389514, "version": 13, "coordinates": [[-87.6277499, 41.8863375], [-87.6273407, 41.8863436], [-87.6273396, 41.8863339], [-87.6271328, 41.8863379], [-87.6270968, 41.8863386], [-87.6270991, 41.8866309], [-87.6271288, 41.8866306], [-87.6272242, 41.8866295], [-87.6273446, 41.8866282], [-87.627346, 41.8866984], [-87.6273463, 41.8867111], [-87.6273478, 41.8867197], [-87.627381, 41.8867192], [-87.6275197, 41.8867167], [-87.6277622, 41.8867154], [-87.6277552, 41.8865163]] }, "parts": [{ "way": 685494066, "version": 4, "coordinates": [[-87.6273446, 41.8866282], [-87.627346, 41.8866984], [-87.6273463, 41.8867111], [-87.6273478, 41.8867197], [-87.627381, 41.8867192], [-87.6275197, 41.8867167], [-87.6277622, 41.8867154], [-87.6277552, 41.8865163], [-87.6277499, 41.8863375], [-87.6273407, 41.8863436]], "bottom": 0, "top": 159 }, { "way": 685494067, "version": 4, "coordinates": [[-87.6273407, 41.8863436], [-87.6273446, 41.8866282], [-87.6272242, 41.8866295], [-87.6271288, 41.8866306], [-87.6270991, 41.8866309], [-87.6270968, 41.8863386], [-87.6271328, 41.8863379], [-87.6273396, 41.8863339]], "bottom": 0, "top": 7.8, "estimatedHeight": true }] },
17
17
  { "id": "building-crain-communications", "name": "Crain Communications Building", "shortName": "Crain", "height": 177.4, "tipHeight": 177.4, "heightSource": "https://www.skyscrapercenter.com/building/150-north-michigan-avenue/2441", "note": "Detailed Crain: mapped parts, split, notches and roof bearing; roof slope and both peaks at the published 177.4 m measured on the source photograph, with its 3.5 m band pitch. Lobby, window module and roof grid estimated.", "footprint": { "way": 210671717, "version": 14, "coordinates": [[-87.6251984, 41.8846185], [-87.6252086, 41.8849637], [-87.6251849, 41.884981], [-87.6251613, 41.8849983], [-87.6252038, 41.8850292], [-87.6247448, 41.8850344], [-87.624744, 41.8850059], [-87.6247427, 41.8849565], [-87.6247379, 41.8847857], [-87.6247375, 41.8847566], [-87.6248159, 41.8846976], [-87.6248301, 41.884687], [-87.6248227, 41.8846815], [-87.6247345, 41.8846233], [-87.624814, 41.8846217], [-87.624879, 41.8846221]] }, "parts": [{ "way": 284816227, "version": 2, "coordinates": [[-87.6250471, 41.8849145], [-87.625093, 41.8848805], [-87.6252086, 41.8849637], [-87.6251849, 41.884981], [-87.6251613, 41.8849983]], "bottom": 0, "top": 152.5 }, { "way": 284816228, "version": 2, "coordinates": [[-87.6247345, 41.8846233], [-87.6248227, 41.8846815], [-87.6248301, 41.884687], [-87.625093, 41.8848805], [-87.6252086, 41.8849637], [-87.6251984, 41.8846185], [-87.624879, 41.8846221], [-87.624814, 41.8846217]], "bottom": 0, "top": 172.4, "roofSlope": { "direction": 133.0, "height": 75.0 } }, { "way": 284816229, "version": 2, "coordinates": [[-87.6251613, 41.8849983], [-87.6250471, 41.8849145], [-87.625093, 41.8848805], [-87.6248301, 41.884687], [-87.6248159, 41.8846976], [-87.6247375, 41.8847566], [-87.6247379, 41.8847857], [-87.6247427, 41.8849565], [-87.624744, 41.8850059], [-87.6247448, 41.8850344], [-87.6252038, 41.8850292]], "bottom": 0, "top": 177.4, "roofSlope": { "direction": 133.0, "height": 73.0 } }] },
18
18
  { "id": "building-330-north-wabash", "name": "330 North Wabash", "shortName": "330 N Wabash", "height": 211.84, "tipHeight": 211.84, "heightSource": "https://en.wikipedia.org/wiki/330_North_Wabash", "note": "Detailed 330 North Wabash (AMA Plaza): the tower the drawing labels Michigan Plaza South, whose roof corners land on the drawn ones through the photograph's camera. Mapped outline and 211.84 m (695 ft) height; bronze I-beam mullions on the 5 ft module, bronze spandrels and glass on 12 ft 6 in floors, a lobby on the 25 ft plaza, and louvered mechanical floors. No drawn model; the original layout has no counterpart.", "footprint": { "way": 64596068, "version": 20, "coordinates": [[-87.6278354, 41.8890249], [-87.627387, 41.8890335], [-87.6273775, 41.888809], [-87.6273647, 41.8885094], [-87.6273551, 41.8882848], [-87.6278057, 41.8882763]] }, "parts": [{ "way": 64596068, "version": 20, "coordinates": [[-87.6278354, 41.8890249], [-87.627387, 41.8890335], [-87.6273775, 41.888809], [-87.6273647, 41.8885094], [-87.6273551, 41.8882848], [-87.6278057, 41.8882763]], "bottom": 0, "top": 211.84 }] },
19
19
  { "id": "building-michigan-plaza-south-tower", "name": "Michigan Plaza South", "shortName": "Michigan Plaza S", "height": 168.6, "tipHeight": 168.6, "heightSource": "https://www.skyscrapercenter.com/building/michigan-plaza-south/2825", "note": "Detailed Michigan Plaza South: mapped outline, published 168.6 m height, and a 44-story glazed grid; window rows and bays estimated.", "footprint": { "way": 127107024, "version": 12, "coordinates": [[-87.6231713, 41.8858935], [-87.6239484, 41.8858842], [-87.6239505, 41.8861715], [-87.6238471, 41.8861735], [-87.6238486, 41.886216], [-87.6238504, 41.8862675], [-87.6235019, 41.8862724], [-87.6235019, 41.8861725], [-87.6231743, 41.8861722]] }, "parts": [{ "way": 127107024, "version": 12, "coordinates": [[-87.6231713, 41.8858935], [-87.6239484, 41.8858842], [-87.6239505, 41.8861715], [-87.6238471, 41.8861735], [-87.6238486, 41.886216], [-87.6238504, 41.8862675], [-87.6235019, 41.8862724], [-87.6235019, 41.8861725], [-87.6231743, 41.8861722]], "bottom": 0, "top": 168.6 }] },