Restore cars in parking lots and garages only
This commit is contained in:
1 parent
e7500c8ef9
commit
3d4b29fedf
6 files changed
+33
-5
No files matched your search
@@ -2,12 +2,14 @@
|
|||||||
|
|
||||||
Выделяете на карте небольшой город или район → по данным OSM строится подробная карта
|
Выделяете на карте небольшой город или район → по данным OSM строится подробная карта
|
||||||
с видом строго сверху: дороги реальной ширины с тротуарами и разметкой, парковки
|
с видом строго сверху: дороги реальной ширины с тротуарами и разметкой, парковки
|
||||||
без машин, деревья, и **у каждого здания — этажи, комнаты, двери, окна, лестницы
|
с машинами, деревья, и **у каждого здания — этажи, комнаты, двери, окна, лестницы
|
||||||
и мебель**, нарисованные простыми геометрическими фигурами, как на плане квартиры.
|
и мебель**, нарисованные простыми геометрическими фигурами, как на плане квартиры.
|
||||||
|
|
||||||
Всё работает в браузере, без сервера: данные берутся из Overpass API, генерация
|
Всё работает в браузере, без сервера: данные берутся из Overpass API, генерация
|
||||||
идёт в Web Worker'ах, готовый мир сохраняется в IndexedDB.
|
идёт в Web Worker'ах, готовый мир сохраняется в IndexedDB.
|
||||||
|
|
||||||
|
Машины появляются только на парковках и в гаражах; вдоль улиц они не генерируются.
|
||||||
|
|
||||||
## Запуск
|
## Запуск
|
||||||
|
|
||||||
Нужен Node.js 18+.
|
Нужен Node.js 18+.
|
||||||
|
|||||||
@@ -186,6 +186,13 @@ export function meetingTable(L: number): Def {
|
|||||||
export const filing = (): Def => ({ kind: 'filing_cabinet', w: 0.5, d: 0.6, tall: true, front: 0.6, shapes: [R(0, 0, 0.5, 0.6, 'metal', 0.02), Ln(-0.2, 0.0, 0.2, 0.0, 'dark')] });
|
export const filing = (): Def => ({ kind: 'filing_cabinet', w: 0.5, d: 0.6, tall: true, front: 0.6, shapes: [R(0, 0, 0.5, 0.6, 'metal', 0.02), Ln(-0.2, 0.0, 0.2, 0.0, 'dark')] });
|
||||||
export const mailboxes = (): Def => ({ kind: 'mailboxes', w: 1.2, d: 0.3, shapes: [R(0, 0, 1.2, 0.3, 'metal', 0.02), Ln(-0.3, -0.14, -0.3, 0.14, 'dark'), Ln(0, -0.14, 0, 0.14, 'dark'), Ln(0.3, -0.14, 0.3, 0.14, 'dark')] });
|
export const mailboxes = (): Def => ({ kind: 'mailboxes', w: 1.2, d: 0.3, shapes: [R(0, 0, 1.2, 0.3, 'metal', 0.02), Ln(-0.3, -0.14, -0.3, 0.14, 'dark'), Ln(0, -0.14, 0, 0.14, 'dark'), Ln(0.3, -0.14, 0.3, 0.14, 'dark')] });
|
||||||
|
|
||||||
|
export function carDef(): Def {
|
||||||
|
return {
|
||||||
|
kind: 'car', w: 1.8, d: 4.4, tags: ['vehicle'],
|
||||||
|
shapes: [R(0, 0, 1.8, 4.4, 'car', 0.45), R(0, -0.75, 1.5, 0.55, 'glass', 0.15), R(0, 1.25, 1.45, 0.4, 'glass', 0.12), R(0, 0.25, 1.5, 1.4, 'dark', 0.2)],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function shift(s: Shape, dx: number, dy: number): Shape {
|
export function shift(s: Shape, dx: number, dy: number): Shape {
|
||||||
if (s.t === 'line') return { ...s, x1: s.x1 + dx, x2: s.x2 + dx, y1: s.y1 + dy, y2: s.y2 + dy };
|
if (s.t === 'line') return { ...s, x1: s.x1 + dx, x2: s.x2 + dx, y1: s.y1 + dy, y2: s.y2 + dy };
|
||||||
return { ...s, x: s.x + dx, y: s.y + dy };
|
return { ...s, x: s.x + dx, y: s.y + dy };
|
||||||
|
|||||||
+8
-2
@@ -11,7 +11,7 @@ import {
|
|||||||
import { Rng, hashString } from '../util/rng';
|
import { Rng, hashString } from '../util/rng';
|
||||||
import { SpatialGrid } from '../util/spatial';
|
import { SpatialGrid } from '../util/spatial';
|
||||||
import { classify, clampLevels, defaultLevels, parseLevels } from './classify';
|
import { classify, clampLevels, defaultLevels, parseLevels } from './classify';
|
||||||
import { treeProp } from './props';
|
import { carShapes, CAR_COLORS, treeProp } from './props';
|
||||||
|
|
||||||
export interface InteriorJob {
|
export interface InteriorJob {
|
||||||
building: Building;
|
building: Building;
|
||||||
@@ -332,6 +332,8 @@ export function buildCity(elements: OsmElement[], bbox: BBox, name: string, seed
|
|||||||
const ang = minAreaRectAngle(outer);
|
const ang = minAreaRectAngle(outer);
|
||||||
const local = outer.map((p) => rotate(p, -ang));
|
const local = outer.map((p) => rotate(p, -ang));
|
||||||
const lb = bboxOf(local);
|
const lb = bboxOf(local);
|
||||||
|
const pr = new Rng(hashString(a.id) ^ seed);
|
||||||
|
const fill = pr.range(0.45, 0.85);
|
||||||
// полоса: ряд мест 5 м, проезд 6 м, ряд 5 м, ...
|
// полоса: ряд мест 5 м, проезд 6 м, ряд 5 м, ...
|
||||||
let y = lb[1] + 0.5;
|
let y = lb[1] + 0.5;
|
||||||
let row = 0;
|
let row = 0;
|
||||||
@@ -341,8 +343,12 @@ export function buildCity(elements: OsmElement[], bbox: BBox, name: string, seed
|
|||||||
if (!corners.every((q) => pointInRings(q, a.rings))) continue;
|
if (!corners.every((q) => pointInRings(q, a.rings))) continue;
|
||||||
const cc = rotate([x + 1.25, y + 2.5], ang);
|
const cc = rotate([x + 1.25, y + 2.5], ang);
|
||||||
if (!inBounds(cc) || blockedByBuilding(cc, 2)) continue;
|
if (!inBounds(cc) || blockedByBuilding(cc, 2)) continue;
|
||||||
// Только разметка парковочных мест.
|
// Разметка и припаркованные машины; вдоль улиц машины не создаём.
|
||||||
props.push({ id: `ps${a.id}_${props.length}`, kind: 'parking_spot', pos: cc, rot: ang, w: 2.5, d: 5, shapes: [{ t: 'line', x1: -1.25, y1: -2.5, x2: -1.25, y2: 2.5, s: 'line' }, { t: 'line', x1: 1.25, y1: -2.5, x2: 1.25, y2: 2.5, s: 'line' }], blocksMove: false, blocksSight: false });
|
props.push({ id: `ps${a.id}_${props.length}`, kind: 'parking_spot', pos: cc, rot: ang, w: 2.5, d: 5, shapes: [{ t: 'line', x1: -1.25, y1: -2.5, x2: -1.25, y2: 2.5, s: 'line' }, { t: 'line', x1: 1.25, y1: -2.5, x2: 1.25, y2: 2.5, s: 'line' }], blocksMove: false, blocksSight: false });
|
||||||
|
if (pr.chance(fill)) {
|
||||||
|
const flip = row % 2 === 1 ? Math.PI : 0;
|
||||||
|
props.push({ id: `c${a.id}_${props.length}`, kind: 'car', pos: cc, rot: ang + flip + pr.range(-0.04, 0.04), w: 1.8, d: 4.4, shapes: carShapes(), blocksMove: true, blocksSight: false, color: pr.pick(CAR_COLORS), tags: ['vehicle'] });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
y += row % 2 === 0 ? 5 + 6 : 5 + 0.0;
|
y += row % 2 === 0 ? 5 + 6 : 5 + 0.0;
|
||||||
row++;
|
row++;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import * as K from './catalog';
|
|||||||
import type { Def } from './catalog';
|
import type { Def } from './catalog';
|
||||||
import { Rng } from '../util/rng';
|
import { Rng } from '../util/rng';
|
||||||
import { distToSegment } from '../util/geom';
|
import { distToSegment } from '../util/geom';
|
||||||
|
import { CAR_COLORS } from './props';
|
||||||
|
|
||||||
export interface PropL {
|
export interface PropL {
|
||||||
prop: Prop;
|
prop: Prop;
|
||||||
@@ -462,6 +463,7 @@ export function furnishFloor(ctx: Ctx): PropL[] {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'garage': {
|
case 'garage': {
|
||||||
|
if (a > 14 && shortSide(room) >= 2.6) inMiddle(room, K.carDef(), 0, undefined, rng.pick(CAR_COLORS));
|
||||||
atWall(room, K.workbench(1.6));
|
atWall(room, K.workbench(1.6));
|
||||||
atWall(room, K.shelf(1.2));
|
atWall(room, K.shelf(1.2));
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,6 +1,17 @@
|
|||||||
import type { Prop, Shape, Vec } from '../types';
|
import type { Prop, Shape, Vec } from '../types';
|
||||||
import { Rng } from '../util/rng';
|
import { Rng } from '../util/rng';
|
||||||
|
|
||||||
|
export const CAR_COLORS = ['#c94c4c', '#3d6fb6', '#e8e8e8', '#2b2b2b', '#8c9298', '#d9c27a', '#4f8a5b', '#b5b9bd', '#6b4a8c', '#e07b39'];
|
||||||
|
|
||||||
|
export function carShapes(): Shape[] {
|
||||||
|
return [
|
||||||
|
{ t: 'rect', x: 0, y: 0, w: 1.8, h: 4.4, r: 0.45, s: 'car' },
|
||||||
|
{ t: 'rect', x: 0, y: -0.75, w: 1.5, h: 0.55, r: 0.15, s: 'glass' }, // лобовое
|
||||||
|
{ t: 'rect', x: 0, y: 1.25, w: 1.45, h: 0.4, r: 0.12, s: 'glass' }, // заднее
|
||||||
|
{ t: 'rect', x: 0, y: 0.25, w: 1.5, h: 1.4, r: 0.2, s: 'dark' }, // крыша
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
export function treeProp(id: string, p: Vec, rng: Rng, t: Record<string, string>): Prop {
|
export function treeProp(id: string, p: Vec, rng: Rng, t: Record<string, string>): Prop {
|
||||||
const scrub = !!t.scrub;
|
const scrub = !!t.scrub;
|
||||||
const r0 = scrub ? rng.range(0.8, 1.6) : t.orchard ? rng.range(1.6, 2.4) : rng.range(2.2, 4.2);
|
const r0 = scrub ? rng.range(0.8, 1.6) : t.orchard ? rng.range(1.6, 2.4) : rng.range(2.2, 4.2);
|
||||||
|
|||||||
@@ -148,8 +148,8 @@ export class Renderer {
|
|||||||
private propLayers(props: Prop[]): Layer[] {
|
private propLayers(props: Prop[]): Layer[] {
|
||||||
const map = new Map<string, Layer>();
|
const map = new Map<string, Layer>();
|
||||||
for (const p of props) {
|
for (const p of props) {
|
||||||
// Старые сохранённые миры также показываем без машин.
|
// В старых мирах скрываем только машины вдоль улиц (префикс cs).
|
||||||
if (p.kind === 'car') continue;
|
if (p.kind === 'car' && !p.roomId && p.id.startsWith('cs')) continue;
|
||||||
const shapes = this.shapesOf(p);
|
const shapes = this.shapesOf(p);
|
||||||
const c = Math.cos(p.rot), s = Math.sin(p.rot);
|
const c = Math.cos(p.rot), s = Math.sin(p.rot);
|
||||||
const T = (x: number, y: number): Vec => [p.pos[0] + x * c - y * s, p.pos[1] + x * s + y * c];
|
const T = (x: number, y: number): Vec => [p.pos[0] + x * c - y * s, p.pos[1] + x * s + y * c];
|
||||||
|
|||||||
Reference in new issue
Block a user