commit 6276459f19f16f116539eedc222458fe984e4505 Author: nikola Date: Tue May 19 14:53:38 2026 +0200 feat: initial commit diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..c808ef0 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,15 @@ +# Project Guidelines + +## Scope +- Ovaj folder sluzi za analizu i koncept uredjenja potkrovlja kuce. +- U njemu drzati proracune kvadrature, troskovnike, skice i lokalne vizuelne materijale. + +## Deliverables +- Procena kvadrature za izolaciju kosog krova. +- Predlog sloja izolacije 15-20 cm sa drvenom zavrsnom oblogom umesto gipsa. +- Pregled aktuelnih cena materijala i rada u Srbiji sa izvorima. +- Koncept uredjenja potkrovlja sa lokalno generisanim vizuelnim prikazima. + +## Local Rules +- Ne unositi privatne ili Codex-only beleske u git. +- Sve privremene fajlove drzati u ovom folderu i obrisati ih na kraju uz potvrdu korisnika. diff --git a/STATUS.md b/STATUS.md new file mode 100644 index 0000000..9857030 --- /dev/null +++ b/STATUS.md @@ -0,0 +1,58 @@ +# Status - 2026-03-25 + +## Gde smo stali +- Formiran je projektni folder: `/home/nikola/codex-cli/projects/potkrovlje-dizajn-2026/` +- Upisan je projektni `AGENTS.md`. +- Pregledane su dostavljene osnove i fotografije potkrovlja. +- Provereno je da lokalno postoje alati za izradu vizuala: `python3`, `node`, `chromium`, `PIL`, `matplotlib`, `numpy`, `convert`. +- Pokrenuta je online provera cena materijala i rada za Srbiju. + +## Radna geometrijska pretpostavka krova +- Iz osnove uzeto: bruto povrsina potkrovlja `79.29 m2`. +- Iz tabele uzeto: terasa `5.89 m2`. +- Na osnovu nacrta i oznake `visina slemena 0.85 m` koristi se radni nagib krova oko `17.4°`. +- Faktor kosine: `1 / cos(17.4°) = 1.048`. + +## Izracunate varijante povrsine za izolaciju kosina +1. Ako se racuna puna bruto projekcija krova: +83.09 m2 kosih povrsina +91.40 m2 sa 10% rezerve + +2. Ako terasa nije pod krovom i oduzima se iz projekcije: +76.92 m2 kosih povrsina +84.61 m2 sa 10% rezerve + +## Kolicine izolacije iz trenutnog proracuna +1. Za 15 cm ukupne izolacije: +- puna bruto varijanta: `12.46 m3` ili `13.71 m3` sa rezervom +- varijanta bez terase: `11.54 m3` ili `12.69 m3` sa rezervom + +2. Za 20 cm ukupne izolacije: +- puna bruto varijanta: `16.62 m3` ili `18.28 m3` sa rezervom +- varijanta bez terase: `15.38 m3` ili `16.92 m3` sa rezervom + +## Tehnicki smer rada koji je planiran za nastavak +- Predlog sistema: izolacija izmedju/ispod rogova 15-20 cm, vazdusna ili kontrolisana distanca prema daskama ako membrana nije potvrdena, parna kontrola sa unutrasnje strane, podkonstrukcija, zavrsna drvena obloga umesto gipsa. +- Verovatno ce kao osnovna preporuka ici varijanta sa kamenom vunom + pametna parna brana + drvena obloga. +- Kao premium alternativa moze se razmotriti drveno vlakno zbog boljeg letnjeg komfora. + +## Novo uradjeno 2026-03-25 +- Zavrsena online provera cena materijala i repera za ruke u Srbiji. +- Napisan glavni izvestaj: `/home/nikola/codex-cli/projects/potkrovlje-dizajn-2026/potkrovlje_izvestaj.md` +- Generisani lokalni koncept-vizuali: + - `/home/nikola/codex-cli/projects/potkrovlje-dizajn-2026/renders/contact-sheet.png` + - `/home/nikola/codex-cli/projects/potkrovlje-dizajn-2026/renders/zoning-plan.png` + - `/home/nikola/codex-cli/projects/potkrovlje-dizajn-2026/renders/render-1-warm-nordic.png` + - `/home/nikola/codex-cli/projects/potkrovlje-dizajn-2026/renders/render-2-soft-japandi.png` + - `/home/nikola/codex-cli/projects/potkrovlje-dizajn-2026/renders/render-3-rustic-studio.png` + +## Zakljucak do sada +- Najracionalnija nabavna kvadratura za krovnu izolaciju je `oko 85 m2`. +- Ako se na licu mesta potvrdi da terasa ipak ulazi u krovni omotac, idi na `91-92 m2`. +- Preporucena tehnicka varijanta je `20 cm` ukupne izolacije + `LDS 5 Silk` ili ekvivalentna aktivna parna kontrola + drvena obloga. +- Za preporucenu varijantu 20 cm realan budzet je `oko 502,000-586,000 RSD`, a bezbednije planiranje `550,000-620,000 RSD`. + +## Sledeci koraci ako korisnik zeli nastavak +- Razraditi jedan izabrani koncept do detaljnijeg rasporeda namestaja. +- Napraviti precizniju nabavnu listu po rolnama, pakovanjima i komadima. +- Po potrebi doraditi render u jednom izabranom stilu. diff --git a/generate_concepts.py b/generate_concepts.py new file mode 100644 index 0000000..37cda41 --- /dev/null +++ b/generate_concepts.py @@ -0,0 +1,258 @@ +from __future__ import annotations + +from pathlib import Path + +from PIL import Image, ImageDraw, ImageFont + + +ROOT = Path("/home/nikola/codex-cli/projects/potkrovlje-dizajn-2026") +OUT = ROOT / "renders" +OUT.mkdir(exist_ok=True) + + +def load_font(size: int, bold: bool = False) -> ImageFont.FreeTypeFont | ImageFont.ImageFont: + paths = [ + "/usr/share/fonts/truetype/dejavu/DejaVuSerif-Bold.ttf" if bold else "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", + "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf" if bold else "/usr/share/fonts/truetype/dejavu/DejaVuSerif.ttf", + ] + for path in paths: + try: + return ImageFont.truetype(path, size) + except OSError: + continue + return ImageFont.load_default() + + +TITLE = load_font(54, bold=True) +SUB = load_font(28, bold=True) +TEXT = load_font(24) +SMALL = load_font(18) + + +def lerp(a: tuple[int, int], b: tuple[int, int], t: float) -> tuple[int, int]: + return (int(a[0] + (b[0] - a[0]) * t), int(a[1] + (b[1] - a[1]) * t)) + + +def draw_planks(draw: ImageDraw.ImageDraw, p1, p2, p3, p4, count: int, color: tuple[int, int, int], width: int = 2) -> None: + for i in range(1, count): + a = lerp(p1, p2, i / count) + b = lerp(p4, p3, i / count) + draw.line([a, b], fill=color, width=width) + + +def draw_brick_wall(draw: ImageDraw.ImageDraw, x1: int, y1: int, x2: int, y2: int, base: tuple[int, int, int], mortar: tuple[int, int, int]) -> None: + draw.rectangle([x1, y1, x2, y2], fill=base) + brick_h = 28 + brick_w = 74 + row = 0 + for y in range(y1, y2, brick_h): + offset = 0 if row % 2 == 0 else brick_w // 2 + for x in range(x1 - offset, x2, brick_w): + draw.rectangle([x, y, min(x + brick_w - 6, x2), min(y + brick_h - 4, y2)], outline=mortar, width=2) + row += 1 + + +def draw_window(draw: ImageDraw.ImageDraw, box, frame, glass, muntin) -> None: + x1, y1, x2, y2 = box + draw.rounded_rectangle(box, radius=6, fill=frame, outline=(80, 58, 38), width=3) + inset = 10 + draw.rectangle([x1 + inset, y1 + inset, x2 - inset, y2 - inset], fill=glass) + mid = (x1 + x2) // 2 + draw.line([(mid, y1 + inset), (mid, y2 - inset)], fill=muntin, width=4) + draw.line([(x1 + inset, (y1 + y2) // 2), (x2 - inset, (y1 + y2) // 2)], fill=(255, 255, 255, 85), width=1) + + +def draw_perspective_room(name: str, palette: dict[str, tuple[int, int, int]], furniture: str) -> None: + img = Image.new("RGB", (1600, 1000), palette["bg"]) + draw = ImageDraw.Draw(img) + + draw.rectangle([0, 0, 1600, 1000], fill=palette["bg"]) + draw.rectangle([0, 0, 1600, 300], fill=palette["sky"]) + + left_roof = [(120, 300), (470, 160), (530, 540), (250, 780)] + right_roof = [(1130, 160), (1480, 300), (1350, 780), (1070, 540)] + back_wall = [(470, 160), (1130, 160), (1300, 300), (1100, 540), (500, 540), (300, 300)] + floor = [(250, 780), (1350, 780), (1100, 540), (500, 540)] + left_knee = [(110, 680), (250, 780), (500, 540), (365, 460)] + right_knee = [(1235, 460), (1100, 540), (1350, 780), (1490, 680)] + + draw.polygon(floor, fill=palette["floor"]) + draw_planks(draw, floor[0], floor[1], floor[2], floor[3], 11, palette["floor_lines"], 3) + draw.polygon(back_wall, fill=palette["brick"]) + draw_brick_wall(draw, 350, 210, 1240, 540, palette["brick"], palette["mortar"]) + draw.polygon(left_knee, fill=palette["knee"]) + draw.polygon(right_knee, fill=palette["knee"]) + draw.polygon(left_roof, fill=palette["ceiling"]) + draw.polygon(right_roof, fill=palette["ceiling"]) + draw_planks(draw, left_roof[0], left_roof[1], left_roof[2], left_roof[3], 12, palette["ceiling_lines"]) + draw_planks(draw, right_roof[1], right_roof[0], right_roof[3], right_roof[2], 12, palette["ceiling_lines"]) + + draw.line([(470, 160), (1130, 160)], fill=palette["beam"], width=18) + for x in [250, 420, 590, 760, 930, 1100, 1270]: + draw.line([(x, 250), (x + 80, 540)], fill=palette["beam_soft"], width=18) + + draw_window(draw, (620, 240, 760, 465), palette["frame"], palette["glass"], palette["muntin"]) + draw_window(draw, (850, 240, 1010, 465), palette["frame"], palette["glass"], palette["muntin"]) + draw.rounded_rectangle((420, 230, 570, 510), radius=8, fill=palette["frame"], outline=(70, 45, 25), width=3) + draw.rectangle((435, 245, 555, 495), fill=palette["glass"]) + draw.line([(490, 245), (490, 495)], fill=palette["muntin"], width=3) + + draw.rectangle((315, 575, 495, 635), fill=palette["storage"]) + draw.rectangle((1110, 575, 1285, 635), fill=palette["storage"]) + + if furniture == "lounge": + draw.rounded_rectangle((540, 650, 980, 760), radius=20, fill=palette["sofa"]) + draw.rectangle((660, 720, 860, 760), fill=palette["sofa_shadow"]) + draw.ellipse((720, 705, 900, 785), fill=palette["table"]) + draw.rectangle((1180, 595, 1290, 720), fill=palette["desk"]) + draw.rectangle((1215, 655, 1260, 765), fill=palette["chair"]) + label = "Koncept 1 Warm Nordic Loft" + note = "Otvoreni lounge + desk zona uz pogled i terasu." + elif furniture == "bedroom": + draw.rounded_rectangle((545, 635, 1045, 760), radius=20, fill=palette["bed"]) + draw.rectangle((565, 615, 1025, 650), fill=palette["headboard"]) + draw.rectangle((630, 660, 780, 735), fill=palette["linen_1"]) + draw.rectangle((800, 660, 950, 735), fill=palette["linen_2"]) + draw.rectangle((340, 610, 450, 705), fill=palette["bench"]) + draw.rectangle((1145, 600, 1265, 710), fill=palette["bench"]) + label = "Koncept 2 Soft Japandi Suite" + note = "Mirna spavaca zona sa niskim plakarima pod kosinama." + else: + draw.rounded_rectangle((560, 660, 930, 760), radius=20, fill=palette["sofa"]) + draw.rectangle((960, 615, 1120, 760), fill=palette["shelf"]) + draw.rectangle((1030, 560, 1185, 720), fill=palette["shelf"]) + draw.ellipse((620, 705, 810, 785), fill=palette["table"]) + draw.rectangle((345, 600, 520, 740), fill=palette["desk"]) + draw.rectangle((390, 700, 455, 790), fill=palette["chair"]) + label = "Koncept 3 Rustic Studio Loft" + note = "Studio i biblioteka sa jacim kontrastom drveta i opeke." + + draw.rectangle((0, 0, 1600, 88), fill=(15, 18, 24)) + draw.text((48, 18), label, font=SUB, fill=(245, 240, 232)) + draw.text((48, 915), note, font=TEXT, fill=(30, 30, 32)) + + chips = [palette["ceiling"], palette["brick"], palette["floor"], palette["storage"], palette["accent"]] + x = 1050 + for chip in chips: + draw.rounded_rectangle((x, 905, x + 56, 961), radius=12, fill=chip) + x += 72 + + draw.text((48, 952), "Koncept render: baziran na dostavljenim fotografijama i osnovi, bez laserskog snimka.", font=SMALL, fill=(72, 72, 76)) + img.save(OUT / f"{name}.png", quality=95) + + +def draw_zoning_plan() -> None: + img = Image.new("RGB", (1400, 1000), (246, 241, 235)) + draw = ImageDraw.Draw(img) + draw.rectangle((120, 140, 1280, 860), outline=(45, 45, 45), width=5, fill=(252, 250, 246)) + draw.rectangle((180, 620, 430, 840), outline=(150, 40, 40), width=6, fill=(242, 238, 234)) + draw.text((215, 705), "Stepenice", font=SUB, fill=(120, 25, 25)) + draw.rectangle((430, 160, 1180, 420), fill=(232, 214, 196), outline=(140, 122, 101), width=4) + draw.text((620, 255), "Lounge + terasa", font=TITLE, fill=(76, 58, 46)) + draw.rectangle((540, 470, 1180, 780), fill=(223, 232, 226), outline=(93, 118, 106), width=4) + draw.text((690, 585), "Spavanje", font=TITLE, fill=(52, 78, 68)) + draw.rectangle((180, 160, 420, 500), fill=(219, 225, 236), outline=(85, 100, 124), width=4) + draw.text((208, 300), "Garderoba /\nodlaganje", font=SUB, fill=(60, 76, 96)) + draw.rectangle((1190, 160, 1235, 830), fill=(212, 201, 186)) + draw.rectangle((165, 160, 210, 830), fill=(212, 201, 186)) + draw.text((80, 90), "Predlog zoniranja potkrovlja", font=TITLE, fill=(28, 28, 28)) + draw.text((120, 900), "Niske ivice pod kosinama: plakar 45-60 cm dubine celom duzinom.", font=TEXT, fill=(40, 40, 40)) + img.save(OUT / "zoning-plan.png", quality=95) + + +PALETTES = { + "render-1-warm-nordic": { + "bg": (245, 240, 233), + "sky": (227, 234, 239), + "floor": (142, 112, 84), + "floor_lines": (95, 71, 49), + "brick": (178, 132, 98), + "mortar": (208, 188, 169), + "knee": (171, 157, 140), + "ceiling": (214, 180, 138), + "ceiling_lines": (162, 123, 84), + "beam": (104, 64, 44), + "beam_soft": (120, 76, 54), + "frame": (126, 88, 52), + "glass": (176, 205, 219), + "muntin": (230, 236, 240), + "storage": (181, 164, 146), + "sofa": (208, 191, 170), + "sofa_shadow": (183, 164, 144), + "table": (208, 178, 137), + "desk": (121, 96, 72), + "chair": (94, 79, 64), + "accent": (162, 121, 84), + "bed": (222, 212, 198), + "headboard": (130, 103, 82), + "linen_1": (236, 229, 220), + "linen_2": (198, 180, 159), + "bench": (168, 136, 104), + "shelf": (116, 88, 67), + }, + "render-2-soft-japandi": { + "bg": (243, 241, 236), + "sky": (226, 231, 228), + "floor": (156, 140, 114), + "floor_lines": (117, 99, 74), + "brick": (194, 184, 171), + "mortar": (228, 223, 214), + "knee": (188, 184, 170), + "ceiling": (224, 210, 183), + "ceiling_lines": (176, 161, 133), + "beam": (124, 96, 70), + "beam_soft": (144, 114, 86), + "frame": (133, 105, 74), + "glass": (190, 205, 202), + "muntin": (235, 239, 237), + "storage": (203, 198, 188), + "sofa": (214, 208, 198), + "sofa_shadow": (184, 177, 167), + "table": (189, 171, 146), + "desk": (156, 142, 122), + "chair": (121, 117, 101), + "accent": (125, 141, 122), + "bed": (219, 213, 203), + "headboard": (150, 138, 118), + "linen_1": (241, 236, 228), + "linen_2": (193, 204, 193), + "bench": (174, 166, 148), + "shelf": (126, 120, 106), + }, + "render-3-rustic-studio": { + "bg": (236, 232, 227), + "sky": (219, 226, 234), + "floor": (116, 98, 86), + "floor_lines": (80, 63, 52), + "brick": (153, 108, 82), + "mortar": (196, 176, 164), + "knee": (111, 110, 108), + "ceiling": (170, 143, 109), + "ceiling_lines": (116, 92, 65), + "beam": (69, 44, 32), + "beam_soft": (86, 58, 40), + "frame": (112, 72, 43), + "glass": (175, 197, 212), + "muntin": (232, 239, 242), + "storage": (96, 96, 94), + "sofa": (138, 121, 105), + "sofa_shadow": (98, 84, 73), + "table": (177, 138, 95), + "desk": (83, 71, 64), + "chair": (56, 57, 58), + "accent": (122, 100, 78), + "bed": (192, 175, 159), + "headboard": (103, 84, 71), + "linen_1": (218, 206, 196), + "linen_2": (143, 132, 118), + "bench": (134, 103, 80), + "shelf": (67, 63, 61), + }, +} + + +if __name__ == "__main__": + draw_zoning_plan() + draw_perspective_room("render-1-warm-nordic", PALETTES["render-1-warm-nordic"], "lounge") + draw_perspective_room("render-2-soft-japandi", PALETTES["render-2-soft-japandi"], "bedroom") + draw_perspective_room("render-3-rustic-studio", PALETTES["render-3-rustic-studio"], "studio") diff --git a/generate_warm_nordic_highres.py b/generate_warm_nordic_highres.py new file mode 100644 index 0000000..bc2539e --- /dev/null +++ b/generate_warm_nordic_highres.py @@ -0,0 +1,508 @@ +from __future__ import annotations + +from pathlib import Path +from typing import Iterable + +import numpy as np +from PIL import Image, ImageChops, ImageDraw, ImageFilter, ImageFont + + +ROOT = Path("/home/nikola/codex-cli/projects/potkrovlje-dizajn-2026") +OUT = ROOT / "za-pregled" / "warm-nordic-highres" +OUT.mkdir(parents=True, exist_ok=True) + + +def font(size: int, bold: bool = False) -> ImageFont.FreeTypeFont | ImageFont.ImageFont: + options = [ + "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf" if bold else "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", + "/usr/share/fonts/truetype/liberation2/LiberationSans-Bold.ttf" if bold else "/usr/share/fonts/truetype/liberation2/LiberationSans-Regular.ttf", + ] + for path in options: + try: + return ImageFont.truetype(path, size) + except OSError: + continue + return ImageFont.load_default() + + +TITLE = font(66, bold=True) +HEAD = font(38, bold=True) +TEXT = font(28) +SMALL = font(22) + + +def add_noise(arr: np.ndarray, strength: float) -> np.ndarray: + noise = np.random.normal(0, strength, arr.shape).astype(np.float32) + out = np.clip(arr.astype(np.float32) + noise, 0, 255) + return out.astype(np.uint8) + + +def vertical_gradient(size: tuple[int, int], top: tuple[int, int, int], bottom: tuple[int, int, int]) -> Image.Image: + w, h = size + base = np.zeros((h, w, 3), dtype=np.uint8) + for i in range(h): + t = i / max(h - 1, 1) + base[i, :, :] = [int(top[c] * (1 - t) + bottom[c] * t) for c in range(3)] + return Image.fromarray(base, "RGB") + + +def wood_texture(size: tuple[int, int], base=(194, 159, 120), dark=(125, 90, 58), direction: str = "horizontal") -> Image.Image: + w, h = size + axis = w if direction == "horizontal" else h + img = np.zeros((h, w, 3), dtype=np.uint8) + grain = np.zeros(axis, dtype=np.float32) + phase = np.random.uniform(0, np.pi) + for i in range(axis): + t = i / axis + wave = ( + 0.55 * np.sin(t * 28 + phase) + + 0.22 * np.sin(t * 91 + 0.7) + + 0.18 * np.sin(t * 160 + 1.9) + ) + grain[i] = wave + grain += np.random.normal(0, 0.09, axis) + grain = (grain - grain.min()) / (grain.max() - grain.min() + 1e-6) + if direction == "horizontal": + val = np.tile(grain, (h, 1)) + else: + val = np.tile(grain[:, None], (1, w)) + for c in range(3): + img[:, :, c] = (dark[c] * (1 - val) + base[c] * val).astype(np.uint8) + img = add_noise(img, 5) + return Image.fromarray(img, "RGB").filter(ImageFilter.GaussianBlur(0.6)) + + +def concrete_texture(size: tuple[int, int], base=(194, 190, 183)) -> Image.Image: + w, h = size + arr = np.full((h, w, 3), base, dtype=np.uint8) + arr = add_noise(arr, 12) + specks = np.random.uniform(0, 1, (h, w)) + arr[specks > 0.995] = np.clip(arr[specks > 0.995] - 30, 0, 255) + return Image.fromarray(arr, "RGB").filter(ImageFilter.GaussianBlur(0.4)) + + +def brick_texture(size: tuple[int, int]) -> Image.Image: + w, h = size + img = Image.new("RGB", size, (206, 191, 177)) + draw = ImageDraw.Draw(img) + brick_h = 42 + brick_w = 105 + shades = [(163, 111, 84), (174, 122, 91), (145, 97, 74), (186, 133, 99)] + for row, y in enumerate(range(0, h, brick_h)): + offset = 0 if row % 2 == 0 else brick_w // 2 + for x in range(-offset, w, brick_w): + c = shades[(row + x // max(brick_w, 1)) % len(shades)] + inset = 4 + draw.rounded_rectangle( + (x + inset, y + inset, x + brick_w - 8, y + brick_h - 6), + radius=3, + fill=c, + ) + return img.filter(ImageFilter.GaussianBlur(0.2)) + + +def linen_texture(size: tuple[int, int], base=(223, 213, 198), accent=(190, 177, 159)) -> Image.Image: + w, h = size + arr = np.zeros((h, w, 3), dtype=np.uint8) + for y in range(h): + for x in range(w): + mix = 0.55 + 0.25 * np.sin(x / 12.0) + 0.2 * np.sin(y / 14.0) + mix = max(0.0, min(1.0, mix)) + arr[y, x] = [int(base[c] * mix + accent[c] * (1 - mix)) for c in range(3)] + arr = add_noise(arr, 9) + return Image.fromarray(arr, "RGB").filter(ImageFilter.GaussianBlur(0.5)) + + +def slat_texture(size: tuple[int, int]) -> Image.Image: + w, h = size + base = Image.new("RGB", size, (74, 67, 62)) + draw = ImageDraw.Draw(base) + wood = wood_texture((46, h), base=(172, 136, 94), dark=(110, 76, 46), direction="vertical") + for x in range(0, w, 56): + base.paste(wood, (x + 8, 0)) + return base.filter(ImageFilter.GaussianBlur(0.3)) + + +def rug_texture(size: tuple[int, int], base=(206, 197, 184), line=(161, 150, 132)) -> Image.Image: + img = linen_texture(size, base, line) + draw = ImageDraw.Draw(img) + w, h = size + for y in range(40, h, 58): + draw.line((40, y, w - 40, y), fill=(line[0] - 8, line[1] - 8, line[2] - 8), width=2) + return img + + +def repeat_texture(tex: Image.Image, size: tuple[int, int]) -> Image.Image: + w, h = size + tw, th = tex.size + out = Image.new("RGB", size) + for y in range(0, h, th): + for x in range(0, w, tw): + out.paste(tex, (x, y)) + return out + + +def find_perspective_coeffs(dst_pts, src_pts): + matrix = [] + for (x, y), (u, v) in zip(dst_pts, src_pts): + matrix.append([x, y, 1, 0, 0, 0, -u * x, -u * y]) + matrix.append([0, 0, 0, x, y, 1, -v * x, -v * y]) + a = np.array(matrix, dtype=np.float64) + b = np.array(src_pts).reshape(8) + coeffs = np.linalg.solve(a, b) + return coeffs + + +def polygon_mask(size: tuple[int, int], pts: list[tuple[int, int]]) -> Image.Image: + mask = Image.new("L", size, 0) + ImageDraw.Draw(mask).polygon(pts, fill=255) + return mask + + +def warp_texture_to_polygon(tex: Image.Image, polygon: list[tuple[int, int]], canvas_size: tuple[int, int]) -> tuple[Image.Image, Image.Image]: + xs = [p[0] for p in polygon] + ys = [p[1] for p in polygon] + min_x, max_x = min(xs), max(xs) + min_y, max_y = min(ys), max(ys) + bw = max_x - min_x + bh = max_y - min_y + local = [(x - min_x, y - min_y) for x, y in polygon] + src = [(0, 0), (tex.size[0], 0), (tex.size[0], tex.size[1]), (0, tex.size[1])] + coeffs = find_perspective_coeffs(local, src) + tiled = repeat_texture(tex, (bw + 20, bh + 20)) + warped = tiled.transform((bw, bh), Image.PERSPECTIVE, coeffs, Image.Resampling.BICUBIC) + layer = Image.new("RGBA", canvas_size, (0, 0, 0, 0)) + mask = polygon_mask((bw, bh), local) + layer.paste(warped.convert("RGBA"), (min_x, min_y), mask) + canvas_mask = Image.new("L", canvas_size, 0) + canvas_mask.paste(mask, (min_x, min_y)) + return layer, canvas_mask + + +def paste_polygon_texture(base: Image.Image, polygon: list[tuple[int, int]], tex: Image.Image, tint: tuple[int, int, int] | None = None, alpha: int = 255) -> None: + layer, mask = warp_texture_to_polygon(tex, polygon, base.size) + if tint is not None: + tint_img = Image.new("RGBA", base.size, (*tint, 0)) + tint_img.putalpha(mask.point(lambda p: int(p * alpha / 255))) + layer = Image.blend(layer, tint_img, 0.18) + base.alpha_composite(layer) + + +def add_polygon_overlay(base: Image.Image, polygon: list[tuple[int, int]], color: tuple[int, int, int], opacity: int) -> None: + overlay = Image.new("RGBA", base.size, (0, 0, 0, 0)) + draw = ImageDraw.Draw(overlay) + draw.polygon(polygon, fill=(*color, opacity)) + base.alpha_composite(overlay) + + +def add_shadow(base: Image.Image, shape_box: tuple[int, int, int, int], offset=(18, 18), blur=24, opacity=80, radius=30) -> None: + shadow = Image.new("RGBA", base.size, (0, 0, 0, 0)) + draw = ImageDraw.Draw(shadow) + x1, y1, x2, y2 = shape_box + draw.rounded_rectangle((x1 + offset[0], y1 + offset[1], x2 + offset[0], y2 + offset[1]), radius=radius, fill=(25, 24, 22, opacity)) + shadow = shadow.filter(ImageFilter.GaussianBlur(blur)) + base.alpha_composite(shadow) + + +def rounded_box(base: Image.Image, box, fill, outline=None, width=1, radius=28, texture: Image.Image | None = None) -> None: + if texture is not None: + mask = Image.new("L", base.size, 0) + ImageDraw.Draw(mask).rounded_rectangle(box, radius=radius, fill=255) + tex = repeat_texture(texture, base.size).convert("RGBA") + tex.putalpha(mask) + base.alpha_composite(tex) + overlay = Image.new("RGBA", base.size, (0, 0, 0, 0)) + draw = ImageDraw.Draw(overlay) + draw.rounded_rectangle(box, radius=radius, fill=fill, outline=outline, width=width) + base.alpha_composite(overlay) + + +def add_text_panel(base: Image.Image, title: str, subtitle: str) -> None: + panel = Image.new("RGBA", base.size, (0, 0, 0, 0)) + draw = ImageDraw.Draw(panel) + draw.rounded_rectangle((56, 48, 1030, 220), radius=30, fill=(244, 239, 230, 220)) + draw.text((92, 78), title, font=HEAD, fill=(34, 34, 34)) + draw.text((94, 138), subtitle, font=TEXT, fill=(82, 76, 68)) + base.alpha_composite(panel) + + +def draw_lounge_render() -> None: + size = (3200, 1800) + base = vertical_gradient(size, (243, 234, 223), (226, 214, 199)).convert("RGBA") + + left_roof = [(100, 450), (860, 170), (1180, 980), (420, 1270)] + right_roof = [(2140, 170), (3100, 450), (2780, 1270), (2020, 980)] + back_wall = [(860, 170), (2140, 170), (2560, 450), (2080, 980), (1180, 980), (640, 450)] + floor = [(420, 1270), (2780, 1270), (2080, 980), (1180, 980)] + + paste_polygon_texture(base, floor, wood_texture((1200, 900), base=(152, 118, 84), dark=(98, 70, 44), direction="horizontal")) + add_polygon_overlay(base, floor, (40, 24, 10), 24) + paste_polygon_texture(base, back_wall, brick_texture((1400, 900))) + add_polygon_overlay(base, back_wall, (86, 67, 48), 18) + paste_polygon_texture(base, left_roof, wood_texture((1600, 1200), base=(215, 184, 147), dark=(160, 122, 84), direction="vertical")) + paste_polygon_texture(base, right_roof, wood_texture((1600, 1200), base=(215, 184, 147), dark=(160, 122, 84), direction="vertical")) + add_polygon_overlay(base, left_roof, (75, 50, 25), 16) + add_polygon_overlay(base, right_roof, (75, 50, 25), 26) + + beam = Image.new("RGBA", size, (0, 0, 0, 0)) + draw = ImageDraw.Draw(beam) + draw.line((840, 200, 2160, 200), fill=(116, 73, 44, 255), width=30) + for x in [660, 920, 1180, 1440, 1700, 1960, 2220, 2480]: + draw.line((x, 280, x + 150, 930), fill=(124, 80, 50, 255), width=24) + base.alpha_composite(beam) + + light = Image.new("RGBA", size, (0, 0, 0, 0)) + draw = ImageDraw.Draw(light) + draw.polygon([(640, 380), (1020, 250), (1060, 1270), (560, 1270)], fill=(255, 243, 214, 38)) + draw.polygon([(0, 820), (520, 710), (1600, 1770), (0, 1770)], fill=(255, 218, 154, 36)) + base.alpha_composite(light.filter(ImageFilter.GaussianBlur(35))) + + wall_overlay = Image.new("RGBA", size, (0, 0, 0, 0)) + dwo = ImageDraw.Draw(wall_overlay) + dwo.rounded_rectangle((860, 330, 1120, 930), radius=12, fill=(140, 97, 60, 255), outline=(104, 68, 41, 255), width=5) + dwo.rectangle((885, 355, 1090, 900), fill=(181, 208, 223, 255)) + dwo.line((988, 355, 988, 900), fill=(235, 241, 245, 255), width=5) + dwo.rounded_rectangle((1360, 325, 1600, 935), radius=12, fill=(138, 95, 58, 255), outline=(100, 66, 39, 255), width=5) + dwo.rectangle((1385, 350, 1570, 905), fill=(189, 209, 220, 255)) + dwo.rounded_rectangle((1640, 345, 1870, 910), radius=12, fill=(138, 95, 58, 255), outline=(100, 66, 39, 255), width=5) + dwo.rectangle((1664, 372, 1844, 880), fill=(192, 212, 222, 255)) + base.alpha_composite(wall_overlay) + + # Central storage / media core + core_front = [(1730, 575), (2070, 575), (2070, 1210), (1730, 1210)] + core_side = [(2070, 575), (2285, 685), (2285, 1325), (2070, 1210)] + paste_polygon_texture(base, core_front, slat_texture((600, 900))) + paste_polygon_texture(base, core_side, wood_texture((500, 900), base=(183, 150, 113), dark=(118, 87, 58), direction="vertical")) + add_polygon_overlay(base, core_front, (46, 38, 30), 22) + add_polygon_overlay(base, core_side, (38, 30, 24), 28) + + storage_draw = ImageDraw.Draw(base) + storage_draw.rounded_rectangle((1770, 660, 2030, 790), radius=12, fill=(28, 28, 30, 255)) + storage_draw.rounded_rectangle((1788, 835, 2015, 960), radius=16, fill=(133, 98, 64, 255)) + storage_draw.rectangle((1810, 870, 1992, 908), fill=(70, 56, 45, 255)) + storage_draw.rounded_rectangle((1768, 1010, 2017, 1165), radius=18, outline=(206, 181, 148, 180), width=3) + storage_draw.line((2190, 745, 2190, 1255), fill=(86, 64, 45, 200), width=3) + + # TV, floating media shelf + rounded_box(base, (1835, 680, 2010, 775), (18, 18, 20, 255), outline=(42, 42, 42, 255), width=4, radius=10) + rounded_box(base, (1815, 805, 2025, 845), (126, 93, 63, 220), radius=10) + + # Coffee nook + add_shadow(base, (920, 935, 1275, 1210), blur=20, opacity=70, radius=24) + rounded_box(base, (920, 935, 1275, 1210), (142, 109, 77, 255), texture=wood_texture((400, 400), base=(162, 126, 92), dark=(109, 79, 53), direction="vertical")) + top = Image.new("RGBA", size, (0, 0, 0, 0)) + ImageDraw.Draw(top).rounded_rectangle((900, 905, 1295, 965), radius=18, fill=(230, 224, 212, 255)) + base.alpha_composite(top) + coffee = Image.new("RGBA", size, (0, 0, 0, 0)) + dc = ImageDraw.Draw(coffee) + dc.rectangle((987, 820, 1180, 900), fill=(188, 170, 142, 220)) + dc.rectangle((1055, 760, 1115, 820), fill=(230, 230, 228, 255)) + dc.ellipse((1070, 730, 1110, 770), fill=(250, 250, 250, 255)) + dc.rounded_rectangle((1210, 855, 1260, 905), radius=10, fill=(72, 72, 72, 255)) + base.alpha_composite(coffee) + + # Sofa and rug + add_shadow(base, (1090, 1110, 1730, 1445), blur=32, opacity=80, radius=46) + rounded_box(base, (1010, 1135, 1760, 1450), (196, 181, 163, 235), texture=linen_texture((900, 500), base=(203, 189, 171), accent=(173, 158, 140))) + rounded_box(base, (1010, 1055, 1450, 1205), (205, 193, 178, 245), texture=linen_texture((600, 260), base=(209, 196, 180), accent=(185, 170, 151))) + rounded_box(base, (1350, 1015, 1745, 1185), (202, 189, 174, 245), texture=linen_texture((550, 260), base=(206, 193, 177), accent=(181, 166, 149))) + add_shadow(base, (1115, 1270, 1615, 1530), blur=25, opacity=60, radius=60) + rounded_box(base, (1125, 1280, 1625, 1525), (235, 227, 214, 215), texture=rug_texture((800, 420))) + add_shadow(base, (1290, 1180, 1510, 1350), blur=20, opacity=55, radius=80) + rounded_box(base, (1295, 1188, 1515, 1348), (188, 153, 112, 235), texture=wood_texture((300, 220), base=(192, 157, 114), dark=(128, 95, 62), direction="horizontal"), radius=90) + + # Sideboard under slope + rounded_box(base, (2440, 980, 2890, 1135), (173, 154, 137, 220), texture=wood_texture((600, 220), base=(177, 152, 124), dark=(119, 94, 68), direction="horizontal")) + deco = Image.new("RGBA", size, (0, 0, 0, 0)) + dd = ImageDraw.Draw(deco) + dd.ellipse((2480, 880, 2570, 1035), fill=(104, 124, 88, 255)) + dd.rectangle((2515, 975, 2535, 1035), fill=(122, 94, 70, 255)) + dd.ellipse((2605, 905, 2660, 990), fill=(205, 186, 166, 255)) + dd.ellipse((2690, 912, 2745, 995), fill=(222, 214, 202, 255)) + base.alpha_composite(deco) + + add_text_panel(base, "Warm Nordic Lounge", "Terrace side: living room, TV wall on storage core, integrated coffee nook.") + + draw = ImageDraw.Draw(base) + draw.text((74, 1660), "Storage koncept: hrastove letvice oko centralnog volumena skrivaju posteljinu, kofer, sezonske stvari i utility zonu.", font=SMALL, fill=(58, 53, 47)) + + base = base.filter(ImageFilter.UnsharpMask(radius=2, percent=130, threshold=2)) + base.save(OUT / "warm-nordic-lounge-render.png", quality=95) + + +def draw_studio_render() -> None: + size = (3200, 1800) + base = vertical_gradient(size, (239, 233, 224), (219, 209, 196)).convert("RGBA") + + left_roof = [(90, 420), (880, 160), (1220, 980), (380, 1290)] + right_roof = [(2060, 160), (3110, 420), (2820, 1290), (1980, 980)] + back_wall = [(880, 160), (2060, 160), (2520, 420), (2040, 980), (1220, 980), (640, 420)] + floor = [(380, 1290), (2820, 1290), (2040, 980), (1220, 980)] + + paste_polygon_texture(base, floor, wood_texture((1200, 900), base=(148, 115, 84), dark=(92, 67, 45), direction="horizontal")) + add_polygon_overlay(base, floor, (30, 18, 8), 34) + paste_polygon_texture(base, back_wall, brick_texture((1400, 900))) + add_polygon_overlay(base, back_wall, (74, 56, 40), 30) + paste_polygon_texture(base, left_roof, wood_texture((1600, 1200), base=(209, 177, 139), dark=(149, 111, 76), direction="vertical")) + paste_polygon_texture(base, right_roof, wood_texture((1600, 1200), base=(209, 177, 139), dark=(149, 111, 76), direction="vertical")) + add_polygon_overlay(base, left_roof, (68, 48, 28), 22) + add_polygon_overlay(base, right_roof, (50, 34, 18), 35) + + beam = Image.new("RGBA", size, (0, 0, 0, 0)) + draw = ImageDraw.Draw(beam) + draw.line((860, 200, 2100, 200), fill=(96, 60, 38, 255), width=28) + for x in [690, 965, 1240, 1515, 1790, 2065, 2340, 2615]: + draw.line((x, 260, x - 160, 960), fill=(105, 68, 44, 255), width=23) + base.alpha_composite(beam) + + win = Image.new("RGBA", size, (0, 0, 0, 0)) + dw = ImageDraw.Draw(win) + for box in [(1180, 325, 1400, 930), (1570, 345, 1795, 920)]: + dw.rounded_rectangle(box, radius=10, fill=(137, 98, 61, 255), outline=(103, 67, 39, 255), width=5) + x1, y1, x2, y2 = box + dw.rectangle((x1 + 24, y1 + 24, x2 - 24, y2 - 24), fill=(190, 210, 222, 255)) + dw.line(((x1 + x2) // 2, y1 + 24, (x1 + x2) // 2, y2 - 24), fill=(234, 240, 244, 255), width=4) + base.alpha_composite(win) + + light = Image.new("RGBA", size, (0, 0, 0, 0)) + dl = ImageDraw.Draw(light) + dl.polygon([(1115, 380), (1835, 395), (1920, 1450), (1020, 1450)], fill=(255, 236, 205, 32)) + base.alpha_composite(light.filter(ImageFilter.GaussianBlur(40))) + + # Storage / acoustic core + core_front = [(930, 560), (1320, 560), (1320, 1240), (930, 1240)] + core_side = [(1320, 560), (1545, 675), (1545, 1358), (1320, 1240)] + paste_polygon_texture(base, core_front, slat_texture((650, 950))) + paste_polygon_texture(base, core_side, wood_texture((400, 950), base=(176, 145, 110), dark=(112, 80, 54), direction="vertical")) + add_polygon_overlay(base, core_front, (38, 33, 29), 28) + add_polygon_overlay(base, core_side, (28, 22, 18), 22) + + drawc = ImageDraw.Draw(base) + drawc.rounded_rectangle((980, 675, 1268, 1010), radius=18, fill=(160, 128, 92, 100), outline=(220, 192, 152, 180), width=3) + drawc.rounded_rectangle((1000, 1035, 1248, 1180), radius=18, outline=(220, 192, 152, 180), width=3) + drawc.rounded_rectangle((1360, 760, 1508, 900), radius=12, fill=(42, 42, 44, 255)) + + # Studio desk + add_shadow(base, (1580, 990, 2500, 1235), blur=24, opacity=82, radius=24) + rounded_box(base, (1580, 990, 2500, 1235), (120, 88, 61, 240), texture=wood_texture((1000, 320), base=(140, 105, 74), dark=(91, 64, 41), direction="horizontal"), radius=16) + desk = Image.new("RGBA", size, (0, 0, 0, 0)) + dd = ImageDraw.Draw(desk) + dd.rounded_rectangle((1755, 830, 2005, 980), radius=16, fill=(28, 28, 30, 255)) + dd.rounded_rectangle((2040, 830, 2295, 980), radius=16, fill=(28, 28, 30, 255)) + dd.rectangle((1715, 905, 2340, 930), fill=(38, 38, 40, 255)) + dd.rectangle((1720, 780, 1785, 990), fill=(45, 45, 47, 255)) + dd.rectangle((2260, 780, 2325, 990), fill=(45, 45, 47, 255)) + dd.rectangle((1910, 870, 2050, 955), fill=(231, 225, 214, 255)) + dd.rectangle((2060, 1040, 2125, 1235), fill=(53, 53, 55, 255)) + base.alpha_composite(desk) + + # Lounge daybed + add_shadow(base, (430, 1090, 1220, 1435), blur=28, opacity=72, radius=40) + rounded_box(base, (430, 1105, 1220, 1435), (191, 178, 161, 235), texture=linen_texture((900, 400), base=(198, 186, 170), accent=(166, 151, 135)), radius=34) + rounded_box(base, (465, 1045, 1150, 1175), (202, 190, 175, 245), texture=linen_texture((760, 180), base=(205, 194, 179), accent=(172, 157, 141)), radius=28) + rounded_box(base, (615, 1240, 900, 1380), (145, 110, 84, 235), texture=linen_texture((360, 180), base=(150, 118, 93), accent=(120, 93, 71)), radius=22) + + # Acoustic wall and guitars + paste_polygon_texture(base, [(2530, 520), (2850, 635), (2850, 1160), (2530, 1060)], slat_texture((500, 700))) + acc = Image.new("RGBA", size, (0, 0, 0, 0)) + da = ImageDraw.Draw(acc) + da.line((2670, 870, 2670, 1160), fill=(99, 74, 52, 255), width=10) + da.ellipse((2630, 785, 2710, 905), outline=(148, 113, 76, 255), width=8) + da.line((2760, 845, 2760, 1140), fill=(99, 74, 52, 255), width=10) + da.ellipse((2720, 760, 2800, 880), outline=(148, 113, 76, 255), width=8) + base.alpha_composite(acc) + + # Low storage under slope + rounded_box(base, (2330, 1080, 2900, 1220), (173, 155, 137, 220), texture=wood_texture((620, 220), base=(178, 151, 123), dark=(118, 93, 67), direction="horizontal"), radius=20) + + # Rug + add_shadow(base, (1520, 1200, 2450, 1490), blur=20, opacity=50, radius=40) + rounded_box(base, (1540, 1210, 2460, 1490), (227, 221, 210, 220), texture=rug_texture((1000, 320), base=(221, 213, 201), line=(175, 162, 145)), radius=28) + + add_text_panel(base, "Warm Nordic Home Studio", "Stair side: recording desk, acoustic slat storage spine, lounge/daybed for listening sessions.") + draw = ImageDraw.Draw(base) + draw.text((74, 1660), "Storage spine: zatvoreni ormari gore za kabaste stvari, otvorene nishe za knjige/ploca, utility rack prema studiju.", font=SMALL, fill=(58, 53, 47)) + + base = base.filter(ImageFilter.UnsharpMask(radius=2, percent=130, threshold=2)) + base.save(OUT / "warm-nordic-studio-render.png", quality=95) + + +def draw_layout_plan() -> None: + size = (2600, 1800) + base = Image.new("RGBA", size, (244, 240, 234, 255)) + draw = ImageDraw.Draw(base) + draw.rounded_rectangle((160, 160, 2440, 1640), radius=24, fill=(248, 245, 241, 255), outline=(66, 60, 55, 255), width=6) + draw.rectangle((300, 1220, 760, 1540), fill=(238, 236, 232, 255), outline=(140, 90, 90, 255), width=5) + draw.text((362, 1350), "Stepenice", font=HEAD, fill=(124, 55, 55)) + draw.rectangle((1040, 460, 1510, 1320), fill=(186, 156, 120, 255), outline=(110, 84, 58, 255), width=5) + draw.text((1080, 780), "Storage\ncore", font=TITLE, fill=(60, 40, 26)) + + # lounge zone + draw.rounded_rectangle((250, 250, 980, 1070), radius=26, fill=(230, 214, 195, 255), outline=(150, 123, 94, 255), width=4) + draw.text((390, 310), "Living / Lounge", font=TITLE, fill=(82, 58, 39)) + draw.rounded_rectangle((330, 655, 740, 820), radius=30, fill=(197, 183, 166, 255)) + draw.rounded_rectangle((565, 875, 790, 995), radius=60, fill=(190, 153, 112, 255)) + draw.rectangle((790, 640, 950, 760), fill=(28, 28, 30, 255)) + draw.rectangle((300, 905, 500, 1025), fill=(145, 110, 80, 255)) + draw.text((300, 1045), "coffee nook", font=TEXT, fill=(90, 65, 45)) + draw.text((750, 785), "TV", font=TEXT, fill=(240, 240, 240)) + + # studio zone + draw.rounded_rectangle((1560, 250, 2320, 1440), radius=26, fill=(220, 227, 221, 255), outline=(109, 128, 112, 255), width=4) + draw.text((1720, 310), "Music Studio", font=TITLE, fill=(57, 80, 65)) + draw.rectangle((1680, 650, 2120, 790), fill=(121, 88, 63, 255)) + draw.rectangle((1760, 520, 1885, 630), fill=(28, 28, 30, 255)) + draw.rectangle((1925, 520, 2050, 630), fill=(28, 28, 30, 255)) + draw.rounded_rectangle((1680, 1030, 2150, 1210), radius=26, fill=(194, 181, 165, 255)) + draw.text((1685, 800), "recording desk", font=TEXT, fill=(49, 40, 33)) + draw.text((1710, 1225), "listening lounge / daybed", font=TEXT, fill=(67, 80, 72)) + + # storage logic + draw.rounded_rectangle((930, 250, 1620, 390), radius=22, fill=(216, 204, 186, 255), outline=(147, 126, 104, 255), width=3) + draw.text((960, 295), "gornji zatvoreni storage za posteljinu, kofere, sezonske stvari", font=TEXT, fill=(69, 56, 43)) + draw.rounded_rectangle((930, 1380, 1620, 1520), radius=22, fill=(216, 204, 186, 255), outline=(147, 126, 104, 255), width=3) + draw.text((995, 1425), "nise / utility / skriveni kablovi / rack", font=TEXT, fill=(69, 56, 43)) + + draw.text((170, 70), "Warm Nordic Loft - finalni koncept rasporeda", font=TITLE, fill=(28, 28, 28)) + draw.text((170, 1700), "Centralni storage volumen resava stubove i odzak kao dizajn feature, a ne kao smetnju.", font=TEXT, fill=(44, 44, 44)) + base.save(OUT / "warm-nordic-layout-plan.png", quality=95) + + +def write_concept_note() -> None: + note = """# Warm Nordic Lounge + Studio + +## Finalni pravac +- terasa strana: living room / lounge +- TV je vezan za centralni storage volumen +- coffee making kutak je uz lounge zonu, blizu terase +- stepenice strana: home music recording studio + listening lounge +- izmedju: storage spine oko stubova i odzaka + +## Kako storage poboljsava ambijent +- obloziti centralni volumen hrastovim vertikalnim letvicama ili finim furnirom +- gornje fronte zatvorene i mirne, push-open, bez vizuelnog nereda +- po jedna otvorena nisa prema svakoj zoni za dekor, knjige, ploce ili kafe set +- TV strana ostaje cista i smirena +- studio strana dobija integrisanu akusticku logiku i skriveni kablovski management + +## Materijali +- svetliji hrast / smoked oak u toplom tonu +- opeka ostaje vidljiva i ociscena +- plafonska drvena obloga prirodan mat ton +- tekstil: lan, greige, pesak, maslina +- metalni detalji: crni ili tamno bronzani + +## Napomena +Ovi vizuali su high-res textured concept renders, ne pravi V-Ray / Corona / AI photo renders. +Za potpuno fotorealistican rezultat na tacnoj geometriji treba ili: +1. originalne fotografije kao lokalni fajlovi za photomontage, ili +2. 3D model u SketchUp / Blender + render engine. +""" + (OUT / "warm-nordic-koncept.md").write_text(note) + + +if __name__ == "__main__": + draw_layout_plan() + draw_lounge_render() + draw_studio_render() + write_concept_note() diff --git a/potkrovlje_izvestaj.md b/potkrovlje_izvestaj.md new file mode 100644 index 0000000..67f4adf --- /dev/null +++ b/potkrovlje_izvestaj.md @@ -0,0 +1,162 @@ +# Potkrovlje - izolacija krova i koncept uredjenja + +Datum provere cena: 2026-03-25 + +## 1. Sta sam uzeo kao bazu +- Bruto povrsina iz dostavljene tabele: `79.29 m2` +- Povrsina terase iz tabele: `5.89 m2` +- Za krov sam radio dve varijante: + - ako terasa nije pod krovom: `76.92 m2` kosina + - ako puna bruto projekcija ulazi u krov: `83.09 m2` kosina +- Za nabavku sam kao realan radni scenario uzeo: + - `84.61 m2` sa 10% rezerve u varijanti bez terase pod krovom + - gornji bezbedni scenario: `91.40 m2` sa 10% rezerve + +## 2. Moja tehnicka preporuka + +### Preporuceni sistem +Od spolja ka unutra: +1. postojeci crep +2. postojeca paropropusna folija +3. postojece daske +4. proverena ventilisana distanca 3-5 cm ako membrana nije sigurno potvrdena kao ispravna i dovoljno difuzno otvorena +5. 10 cm kamena vuna izmedju rogova +6. dodatnih 5 cm ili 10 cm ispod rogova u kontinuitetu +7. unutrasnja aktivna parna brana / pametna membrana ili bar kvalitetna parna brana sa zadihtovanim spojnicama +8. podkonstrukcija za instalacioni sloj +9. drvena obloga - lamperija ili brodski pod, ne gips + +### Sta je najpametnije za tvoj slucaj +- Ako hoces balans cena / trajnost / zvuk / protivpozarno: `20 cm ukupno` mineralne vune i drvena obloga. +- Ako hoces da ustedis, ali da i dalje bude korektno: `15 cm ukupno`. +- Ako membrana ispod crepa nije proverena ili je starija i nepoznata, ja ne bih nabijao vunu direktno u daske bez kontrole detalja. + +### Ocekivani termicki nivo +- `15 cm` mineralne vune: okvirno `U oko 0.23-0.26 W/m2K` +- `20 cm` mineralne vune: okvirno `U oko 0.18-0.21 W/m2K` + +Zakljucak: za potkrovlje koje zelis da koristis kao ozbiljan stambeni prostor, ja bih isao na `20 cm`. + +## 3. Kvadratura i kolicine + +### Radna nabavna kvadratura +- Preporuka za obracun i porucivanje: `85 m2` +- Ako zelis potpuno bezbednu gornju rezervu: `91-92 m2` + +### Zapremina izolacije +- Za `15 cm`: oko `12.69 m3` izolacije sa rezervom +- Za `20 cm`: oko `16.92 m3` izolacije sa rezervom + +## 4. Cene sa neta i kako sam ih citao + +### Izolacija i membrane +- `NaturBoard FIT Plus Knauf` na Roma: `od 436 RSD/kom`, proizvod za kosi krov, kamena vuna. Na Knauf oficijelnom listu za varijantu 100 mm stoji ploca `600 x 1000 mm`, 6 kom u paketu = `3.60 m2/pak`. Iz toga izlazi maloprodajni reper od oko `727 RSD/m2` za `10 cm`. +- `KNAUF PARNA BRANA LDS 5 Silk 75m2` na Roma: `10,030 RSD/rolna` +- `KNAUF PARNA BRANA LDS 200 AluPlus 75m2` na Roma: `8,685 RSD/rolna` +- `Knauf HomeSeal LDS Solifit 1 25m` na Roma: `2,050 RSD/rolna` + +### Drvena podkonstrukcija i obloga +- `Letva 3x5 3m` na Crafter: `259 RSD/kom` +- `Lamperija severna jela` na Krajina drvo / KP: `1,370 RSD/m2` +- `Camova lamperija 10 mm` na Krajina drvo: `1,000 RSD/m2` +- Premium sauna omorika na Aquaplan: `460 RSD/m duzni`, a za `1 m2` treba `10.52 m duznih`, sto izadje oko `4,839 RSD/m2` + +### Ruke - reperi sa trzista +- Daibau za izolaciju potkrovlja daje vise stavki po m2; za `NaturBoard FIT PLUS` prikaz je `5.30-7.90 EUR/m2`, a za `Homeseal LDS 0.02` oko `0.80 EUR/m2` +- Svezaskele navodi prosecnu cenu izolacije potkrovlja `7-12 EUR/m2 plus materijal` + +### Sta iz ovoga zakljucujem +- Cene materijala za Srbiju koje sam nasao online podrzavaju realan raspon: + - izolacija `15 cm`: oko `1,100-1,300 RSD/m2` + - izolacija `20 cm`: oko `1,450-1,700 RSD/m2` + - kvalitetna unutrasnja membrana: oko `135-200 RSD/m2` po rolnama, ali sa realnim nabavnim otpadom racunaj vise + - drvena obloga: realno `1,000-1,370 RSD/m2` za normalnu enterijersku lamperiju + +## 5. Troskovnik koji bih ja koristio za odluku + +Napomena: ovo je `procena`, ne ponuda izvodjaca. U total nisam stavljao teske opravke krova, zamenu trulih dasaka, novu foliju spolja, skelu spolja ili dekorativnu rasvetu. + +### Varijanta A - korektna i racionalna, 15 cm +- Obracunska povrsina: `84.61 m2` +- Materijal okvirno: `288,000-311,000 RSD` +- Ruke okvirno: `129,000-179,000 RSD` +- Ukupno okvirno: `418,000-490,000 RSD` + +Sta dobijas: +- 10 cm izmedju rogova + 5 cm dodatni sloj +- parna brana / aktivna membrana +- podkonstrukcija +- drvena lamperija kao final + +### Varijanta B - moja preporuka, 20 cm +- Obracunska povrsina: `84.61 m2` +- Materijal okvirno: `354,000-378,000 RSD` +- Ruke okvirno: `148,000-208,000 RSD` +- Ukupno okvirno: `502,000-586,000 RSD` + +Sta dobijas: +- 20 cm ukupne izolacije +- bolji zimski komfor i bolju zastitu leti +- manji rizik da kasnije pozelis jos jedan dodatni sloj + +### Gornji scenario ako se na licu mesta ispostavi da treba racunati punih 91-92 m2 +- 15 cm: oko `447,000-524,000 RSD` +- 20 cm: oko `535,000-625,000 RSD` + +### Moj savet za budzet +- Ako zelis da uradis jednom i mirno: planiraj `550,000-620,000 RSD` za preporucenu varijantu 20 cm. +- Ako zelis da ne preteras, ali da bude dobro: planiraj `440,000-500,000 RSD` za 15 cm. +- Obe varijante dodatno zastiti sa jos `5-10%` rezerve za nepredvidjene detalje oko terasnih vrata, prodora, stepenisnog otvora i eventualnih krivih rogova. + +## 6. Dizajn preporuka za samo potkrovlje + +## Glavna ideja koju preporucujem +`Warm Nordic Loft` je najbolja osnova za tvoj prostor jer vec imas: +- lepu opeku na zabatima +- vidljivu drvenu krovnu konstrukciju kao karakter +- dobru prirodnu svetlost i pogled + +Zato ne bih pravio "teski" rustik niti hladan minimalizam. Isao bih na: +- svetlu drvenu oblogu plafona u mat ulju +- ociscenu i prelakiranu opeku ili blago fugovanje, ne potpuno zatvaranje +- pod u srednje toplom hrastu ili kvalitetnom SPC/engineered oak tonu +- niske plakare pod obe kosine, dubina `45-60 cm` +- crna ili tamno bronzana rasveta po slemenu i na zabatima +- tekstil u toplim zemljanim tonovima: pesak, maslina, glina, siva lanena + +## Predlog rasporeda +- Zona uz terasu i pogled: lounge + citaonica + mali radni sto +- Mirniji kraj uz dva prozora: spavaca zona +- Pod kosinama sa obe strane: neprekinuti niski plakari +- Oko stepenisnog otvora: lagana ograda od drveta i metala, ne puna zidna pregrada + +## 7. Lokalni koncept renderi i skice + +Napravio sam lokalne koncept-vizuale u folderu `renders/`. To nisu fotorealisticki renderi iz SketchUp/3ds Max merenog modela, nego namerno ciste koncept ilustracije bazirane na tvojoj osnovi i fotografijama, da mozemo brzo da zakljucamo pravac. + +Fajlovi: +- `renders/contact-sheet.png` +- `renders/zoning-plan.png` +- `renders/render-1-warm-nordic.png` +- `renders/render-2-soft-japandi.png` +- `renders/render-3-rustic-studio.png` + +## 8. Sta bih ja uradio prvo na terenu +1. proverio stanje folije i dasaka ispod crepa +2. izmerio stvarnu dubinu rogova i raster +3. proverio gde tacno moze da ide ventilacioni kanal +4. odlucio da li zadrzavas potpuno otvoren loft ili hoces pregradu za spavaci deo +5. tek onda zatvorio finalnu debljinu izolacije i tacnu nabavnu listu + +## 9. Izvori +- Knauf roof insulation: https://www.knaufinsulation.com/what-we-do/our-solutions/mineral-wool-insulation-for-buildings/roof-insulation +- Pro Clima Intello: https://www2.proclima.com/products/internal-sealing/intello +- Roma NaturBoard FIT Plus: https://roma.rs/naturboard-fit-plus-knauf-4003010 +- Knauf oficijelni NaturBoard FIT Plus data page: https://knauf.com/sq-XK/p/produkt/naturboard-fit-plus-23858_4217 +- Roma izolacija krova i parne brane: https://roma.rs/izolacija-krova +- Crafter letva 3x5 3m: https://www.crafter.rs/letva-3x5-3m-090100302 +- Krajina drvo lamperija: https://krajinadrvo.rs/proizvodi/brodski-pod-i-lamperija/lamperija/ +- Aquaplan lamperija od finske omorike: https://www.aquaplanshop.rs/obloge-za-saune/lamperija-od-finske-omorike-14x95mm.html +- Daibau - pregled cena i smernice: https://www.daibau.rs/clanak/1857/izolacija_potkrovlja_-_stiropor_ili_mineralna_vuna +- Daibau - cenovni reperi po m2: https://www.daibau.rs/imenik/izolacija_potkrovja/trgoviste +- Svezaskele izolacija potkrovlja: https://www.svezaskele.rs/izolacija-potkrovlja/ diff --git a/renders/contact-sheet.png b/renders/contact-sheet.png new file mode 100644 index 0000000..29ace07 Binary files /dev/null and b/renders/contact-sheet.png differ diff --git a/renders/render-1-warm-nordic.png b/renders/render-1-warm-nordic.png new file mode 100644 index 0000000..daeb30f Binary files /dev/null and b/renders/render-1-warm-nordic.png differ diff --git a/renders/render-2-soft-japandi.png b/renders/render-2-soft-japandi.png new file mode 100644 index 0000000..922dddf Binary files /dev/null and b/renders/render-2-soft-japandi.png differ diff --git a/renders/render-3-rustic-studio.png b/renders/render-3-rustic-studio.png new file mode 100644 index 0000000..2b4f8bc Binary files /dev/null and b/renders/render-3-rustic-studio.png differ diff --git a/renders/zoning-plan.png b/renders/zoning-plan.png new file mode 100644 index 0000000..207c3ae Binary files /dev/null and b/renders/zoning-plan.png differ diff --git a/za-pregled/START_HERE.md b/za-pregled/START_HERE.md new file mode 100644 index 0000000..c9c96a3 --- /dev/null +++ b/za-pregled/START_HERE.md @@ -0,0 +1,20 @@ +# Potkrovlje - pregled fajlova + +Otvori ovim redom: + +1. `contact-sheet.png` +2. `potkrovlje_izvestaj.md` +3. `zoning-plan.png` +4. `render-1-warm-nordic.png` +5. `render-2-soft-japandi.png` +6. `render-3-rustic-studio.png` + +Brzi orijentir: +- `Warm Nordic Loft` je moja glavna preporuka +- `Soft Japandi Suite` je mirnija i svetlija varijanta +- `Rustic Studio Loft` je tamnija i dramaticnija varijanta + +Kad pregledas, sledeci korak je da izaberes: +- jedan stil +- jednu funkciju prostora: vise spavanje, vise lounge, ili mesovito +- da li hoces 15 cm ili 20 cm izolacije diff --git a/za-pregled/contact-sheet.png b/za-pregled/contact-sheet.png new file mode 100644 index 0000000..29ace07 Binary files /dev/null and b/za-pregled/contact-sheet.png differ diff --git a/za-pregled/potkrovlje_izvestaj.md b/za-pregled/potkrovlje_izvestaj.md new file mode 100644 index 0000000..67f4adf --- /dev/null +++ b/za-pregled/potkrovlje_izvestaj.md @@ -0,0 +1,162 @@ +# Potkrovlje - izolacija krova i koncept uredjenja + +Datum provere cena: 2026-03-25 + +## 1. Sta sam uzeo kao bazu +- Bruto povrsina iz dostavljene tabele: `79.29 m2` +- Povrsina terase iz tabele: `5.89 m2` +- Za krov sam radio dve varijante: + - ako terasa nije pod krovom: `76.92 m2` kosina + - ako puna bruto projekcija ulazi u krov: `83.09 m2` kosina +- Za nabavku sam kao realan radni scenario uzeo: + - `84.61 m2` sa 10% rezerve u varijanti bez terase pod krovom + - gornji bezbedni scenario: `91.40 m2` sa 10% rezerve + +## 2. Moja tehnicka preporuka + +### Preporuceni sistem +Od spolja ka unutra: +1. postojeci crep +2. postojeca paropropusna folija +3. postojece daske +4. proverena ventilisana distanca 3-5 cm ako membrana nije sigurno potvrdena kao ispravna i dovoljno difuzno otvorena +5. 10 cm kamena vuna izmedju rogova +6. dodatnih 5 cm ili 10 cm ispod rogova u kontinuitetu +7. unutrasnja aktivna parna brana / pametna membrana ili bar kvalitetna parna brana sa zadihtovanim spojnicama +8. podkonstrukcija za instalacioni sloj +9. drvena obloga - lamperija ili brodski pod, ne gips + +### Sta je najpametnije za tvoj slucaj +- Ako hoces balans cena / trajnost / zvuk / protivpozarno: `20 cm ukupno` mineralne vune i drvena obloga. +- Ako hoces da ustedis, ali da i dalje bude korektno: `15 cm ukupno`. +- Ako membrana ispod crepa nije proverena ili je starija i nepoznata, ja ne bih nabijao vunu direktno u daske bez kontrole detalja. + +### Ocekivani termicki nivo +- `15 cm` mineralne vune: okvirno `U oko 0.23-0.26 W/m2K` +- `20 cm` mineralne vune: okvirno `U oko 0.18-0.21 W/m2K` + +Zakljucak: za potkrovlje koje zelis da koristis kao ozbiljan stambeni prostor, ja bih isao na `20 cm`. + +## 3. Kvadratura i kolicine + +### Radna nabavna kvadratura +- Preporuka za obracun i porucivanje: `85 m2` +- Ako zelis potpuno bezbednu gornju rezervu: `91-92 m2` + +### Zapremina izolacije +- Za `15 cm`: oko `12.69 m3` izolacije sa rezervom +- Za `20 cm`: oko `16.92 m3` izolacije sa rezervom + +## 4. Cene sa neta i kako sam ih citao + +### Izolacija i membrane +- `NaturBoard FIT Plus Knauf` na Roma: `od 436 RSD/kom`, proizvod za kosi krov, kamena vuna. Na Knauf oficijelnom listu za varijantu 100 mm stoji ploca `600 x 1000 mm`, 6 kom u paketu = `3.60 m2/pak`. Iz toga izlazi maloprodajni reper od oko `727 RSD/m2` za `10 cm`. +- `KNAUF PARNA BRANA LDS 5 Silk 75m2` na Roma: `10,030 RSD/rolna` +- `KNAUF PARNA BRANA LDS 200 AluPlus 75m2` na Roma: `8,685 RSD/rolna` +- `Knauf HomeSeal LDS Solifit 1 25m` na Roma: `2,050 RSD/rolna` + +### Drvena podkonstrukcija i obloga +- `Letva 3x5 3m` na Crafter: `259 RSD/kom` +- `Lamperija severna jela` na Krajina drvo / KP: `1,370 RSD/m2` +- `Camova lamperija 10 mm` na Krajina drvo: `1,000 RSD/m2` +- Premium sauna omorika na Aquaplan: `460 RSD/m duzni`, a za `1 m2` treba `10.52 m duznih`, sto izadje oko `4,839 RSD/m2` + +### Ruke - reperi sa trzista +- Daibau za izolaciju potkrovlja daje vise stavki po m2; za `NaturBoard FIT PLUS` prikaz je `5.30-7.90 EUR/m2`, a za `Homeseal LDS 0.02` oko `0.80 EUR/m2` +- Svezaskele navodi prosecnu cenu izolacije potkrovlja `7-12 EUR/m2 plus materijal` + +### Sta iz ovoga zakljucujem +- Cene materijala za Srbiju koje sam nasao online podrzavaju realan raspon: + - izolacija `15 cm`: oko `1,100-1,300 RSD/m2` + - izolacija `20 cm`: oko `1,450-1,700 RSD/m2` + - kvalitetna unutrasnja membrana: oko `135-200 RSD/m2` po rolnama, ali sa realnim nabavnim otpadom racunaj vise + - drvena obloga: realno `1,000-1,370 RSD/m2` za normalnu enterijersku lamperiju + +## 5. Troskovnik koji bih ja koristio za odluku + +Napomena: ovo je `procena`, ne ponuda izvodjaca. U total nisam stavljao teske opravke krova, zamenu trulih dasaka, novu foliju spolja, skelu spolja ili dekorativnu rasvetu. + +### Varijanta A - korektna i racionalna, 15 cm +- Obracunska povrsina: `84.61 m2` +- Materijal okvirno: `288,000-311,000 RSD` +- Ruke okvirno: `129,000-179,000 RSD` +- Ukupno okvirno: `418,000-490,000 RSD` + +Sta dobijas: +- 10 cm izmedju rogova + 5 cm dodatni sloj +- parna brana / aktivna membrana +- podkonstrukcija +- drvena lamperija kao final + +### Varijanta B - moja preporuka, 20 cm +- Obracunska povrsina: `84.61 m2` +- Materijal okvirno: `354,000-378,000 RSD` +- Ruke okvirno: `148,000-208,000 RSD` +- Ukupno okvirno: `502,000-586,000 RSD` + +Sta dobijas: +- 20 cm ukupne izolacije +- bolji zimski komfor i bolju zastitu leti +- manji rizik da kasnije pozelis jos jedan dodatni sloj + +### Gornji scenario ako se na licu mesta ispostavi da treba racunati punih 91-92 m2 +- 15 cm: oko `447,000-524,000 RSD` +- 20 cm: oko `535,000-625,000 RSD` + +### Moj savet za budzet +- Ako zelis da uradis jednom i mirno: planiraj `550,000-620,000 RSD` za preporucenu varijantu 20 cm. +- Ako zelis da ne preteras, ali da bude dobro: planiraj `440,000-500,000 RSD` za 15 cm. +- Obe varijante dodatno zastiti sa jos `5-10%` rezerve za nepredvidjene detalje oko terasnih vrata, prodora, stepenisnog otvora i eventualnih krivih rogova. + +## 6. Dizajn preporuka za samo potkrovlje + +## Glavna ideja koju preporucujem +`Warm Nordic Loft` je najbolja osnova za tvoj prostor jer vec imas: +- lepu opeku na zabatima +- vidljivu drvenu krovnu konstrukciju kao karakter +- dobru prirodnu svetlost i pogled + +Zato ne bih pravio "teski" rustik niti hladan minimalizam. Isao bih na: +- svetlu drvenu oblogu plafona u mat ulju +- ociscenu i prelakiranu opeku ili blago fugovanje, ne potpuno zatvaranje +- pod u srednje toplom hrastu ili kvalitetnom SPC/engineered oak tonu +- niske plakare pod obe kosine, dubina `45-60 cm` +- crna ili tamno bronzana rasveta po slemenu i na zabatima +- tekstil u toplim zemljanim tonovima: pesak, maslina, glina, siva lanena + +## Predlog rasporeda +- Zona uz terasu i pogled: lounge + citaonica + mali radni sto +- Mirniji kraj uz dva prozora: spavaca zona +- Pod kosinama sa obe strane: neprekinuti niski plakari +- Oko stepenisnog otvora: lagana ograda od drveta i metala, ne puna zidna pregrada + +## 7. Lokalni koncept renderi i skice + +Napravio sam lokalne koncept-vizuale u folderu `renders/`. To nisu fotorealisticki renderi iz SketchUp/3ds Max merenog modela, nego namerno ciste koncept ilustracije bazirane na tvojoj osnovi i fotografijama, da mozemo brzo da zakljucamo pravac. + +Fajlovi: +- `renders/contact-sheet.png` +- `renders/zoning-plan.png` +- `renders/render-1-warm-nordic.png` +- `renders/render-2-soft-japandi.png` +- `renders/render-3-rustic-studio.png` + +## 8. Sta bih ja uradio prvo na terenu +1. proverio stanje folije i dasaka ispod crepa +2. izmerio stvarnu dubinu rogova i raster +3. proverio gde tacno moze da ide ventilacioni kanal +4. odlucio da li zadrzavas potpuno otvoren loft ili hoces pregradu za spavaci deo +5. tek onda zatvorio finalnu debljinu izolacije i tacnu nabavnu listu + +## 9. Izvori +- Knauf roof insulation: https://www.knaufinsulation.com/what-we-do/our-solutions/mineral-wool-insulation-for-buildings/roof-insulation +- Pro Clima Intello: https://www2.proclima.com/products/internal-sealing/intello +- Roma NaturBoard FIT Plus: https://roma.rs/naturboard-fit-plus-knauf-4003010 +- Knauf oficijelni NaturBoard FIT Plus data page: https://knauf.com/sq-XK/p/produkt/naturboard-fit-plus-23858_4217 +- Roma izolacija krova i parne brane: https://roma.rs/izolacija-krova +- Crafter letva 3x5 3m: https://www.crafter.rs/letva-3x5-3m-090100302 +- Krajina drvo lamperija: https://krajinadrvo.rs/proizvodi/brodski-pod-i-lamperija/lamperija/ +- Aquaplan lamperija od finske omorike: https://www.aquaplanshop.rs/obloge-za-saune/lamperija-od-finske-omorike-14x95mm.html +- Daibau - pregled cena i smernice: https://www.daibau.rs/clanak/1857/izolacija_potkrovlja_-_stiropor_ili_mineralna_vuna +- Daibau - cenovni reperi po m2: https://www.daibau.rs/imenik/izolacija_potkrovja/trgoviste +- Svezaskele izolacija potkrovlja: https://www.svezaskele.rs/izolacija-potkrovlja/ diff --git a/za-pregled/render-1-warm-nordic.png b/za-pregled/render-1-warm-nordic.png new file mode 100644 index 0000000..daeb30f Binary files /dev/null and b/za-pregled/render-1-warm-nordic.png differ diff --git a/za-pregled/render-2-soft-japandi.png b/za-pregled/render-2-soft-japandi.png new file mode 100644 index 0000000..922dddf Binary files /dev/null and b/za-pregled/render-2-soft-japandi.png differ diff --git a/za-pregled/render-3-rustic-studio.png b/za-pregled/render-3-rustic-studio.png new file mode 100644 index 0000000..2b4f8bc Binary files /dev/null and b/za-pregled/render-3-rustic-studio.png differ diff --git a/za-pregled/warm-nordic-highres/START_HERE.md b/za-pregled/warm-nordic-highres/START_HERE.md new file mode 100644 index 0000000..47e42ea --- /dev/null +++ b/za-pregled/warm-nordic-highres/START_HERE.md @@ -0,0 +1,19 @@ +# Warm Nordic - pregled + +Otvori ovim redom: + +1. `warm-nordic-contact-sheet.png` +2. `warm-nordic-layout-plan.png` +3. `warm-nordic-lounge-render.png` +4. `warm-nordic-studio-render.png` +5. `warm-nordic-koncept.md` + +Sta gledas: +- lounge strana prema terasi sa TV-om i coffee nook +- studio strana prema stepenicama sa recording desk-om i listening lounge +- centralni storage volumen oko stubova i odzaka kao dizajn feature + +Ako zelis sledeci krug dorade, biramo: +- vise svetla / vise tamnog kontrasta +- vise "soft nordic" ili vise "clean luxury" +- koliko otvoren ili zatvoren da bude storage core diff --git a/za-pregled/warm-nordic-highres/warm-nordic-contact-sheet.png b/za-pregled/warm-nordic-highres/warm-nordic-contact-sheet.png new file mode 100644 index 0000000..c6d337f Binary files /dev/null and b/za-pregled/warm-nordic-highres/warm-nordic-contact-sheet.png differ diff --git a/za-pregled/warm-nordic-highres/warm-nordic-koncept.md b/za-pregled/warm-nordic-highres/warm-nordic-koncept.md new file mode 100644 index 0000000..ee38134 --- /dev/null +++ b/za-pregled/warm-nordic-highres/warm-nordic-koncept.md @@ -0,0 +1,28 @@ +# Warm Nordic Lounge + Studio + +## Finalni pravac +- terasa strana: living room / lounge +- TV je vezan za centralni storage volumen +- coffee making kutak je uz lounge zonu, blizu terase +- stepenice strana: home music recording studio + listening lounge +- izmedju: storage spine oko stubova i odzaka + +## Kako storage poboljsava ambijent +- obloziti centralni volumen hrastovim vertikalnim letvicama ili finim furnirom +- gornje fronte zatvorene i mirne, push-open, bez vizuelnog nereda +- po jedna otvorena nisa prema svakoj zoni za dekor, knjige, ploce ili kafe set +- TV strana ostaje cista i smirena +- studio strana dobija integrisanu akusticku logiku i skriveni kablovski management + +## Materijali +- svetliji hrast / smoked oak u toplom tonu +- opeka ostaje vidljiva i ociscena +- plafonska drvena obloga prirodan mat ton +- tekstil: lan, greige, pesak, maslina +- metalni detalji: crni ili tamno bronzani + +## Napomena +Ovi vizuali su high-res textured concept renders, ne pravi V-Ray / Corona / AI photo renders. +Za potpuno fotorealistican rezultat na tacnoj geometriji treba ili: +1. originalne fotografije kao lokalni fajlovi za photomontage, ili +2. 3D model u SketchUp / Blender + render engine. diff --git a/za-pregled/warm-nordic-highres/warm-nordic-layout-plan.png b/za-pregled/warm-nordic-highres/warm-nordic-layout-plan.png new file mode 100644 index 0000000..fceec6a Binary files /dev/null and b/za-pregled/warm-nordic-highres/warm-nordic-layout-plan.png differ diff --git a/za-pregled/warm-nordic-highres/warm-nordic-lounge-render.png b/za-pregled/warm-nordic-highres/warm-nordic-lounge-render.png new file mode 100644 index 0000000..6cbb572 Binary files /dev/null and b/za-pregled/warm-nordic-highres/warm-nordic-lounge-render.png differ diff --git a/za-pregled/warm-nordic-highres/warm-nordic-studio-render.png b/za-pregled/warm-nordic-highres/warm-nordic-studio-render.png new file mode 100644 index 0000000..3c1ab0d Binary files /dev/null and b/za-pregled/warm-nordic-highres/warm-nordic-studio-render.png differ diff --git a/za-pregled/zoning-plan.png b/za-pregled/zoning-plan.png new file mode 100644 index 0000000..207c3ae Binary files /dev/null and b/za-pregled/zoning-plan.png differ