feat: initial commit

This commit is contained in:
nikola
2026-05-19 14:53:38 +02:00
commit 6276459f19
23 changed files with 1230 additions and 0 deletions
+15
View File
@@ -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.
+58
View File
@@ -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.
+258
View File
@@ -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")
+508
View File
@@ -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()
+162
View File
@@ -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/
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

+20
View File
@@ -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
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

+162
View File
@@ -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/
Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

@@ -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
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 MiB

@@ -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.
Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB