{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "206a681f-a3b4-430d-8e88-d4b11a9f32d0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 38456 characters]",
      "text/plain": [
       "<Figure size 600x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==== Grid Infill 80.0% ====\n",
      "XY Total Area         = 87.0324 mm²\n",
      "  Shell Area          = 31.1879 mm²\n",
      "  Infill Area         = 55.8445 mm²\n",
      "I_x                   = 764.6175\n",
      "I_y                   = 764.6175\n",
      "I_xy                  = 0.0000\n",
      "Polar moment, J       = 1529.2350\n",
      "-- Cross-sections through Z (pattern constant over Z) --\n",
      "Length along X @ y=1.000 mm: 7.4593 mm\n",
      "Length along Y @ x=-2.000 mm: 8.6108 mm\n",
      "Area of XZ plane @ y=1.000: 149.1865 mm²  (Z=20.000 mm)\n",
      "Area of YZ plane @ x=-2.000: 172.2153 mm²  (Z=20.000 mm)\n",
      "\n",
      "A_xz(y=1mm) = 149.18648310388022 mm^2\n",
      "A_yz(x=-2mm) = 172.21526908635838 mm^2\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# ------------------------------\n",
    "# Geometry utilities\n",
    "# ------------------------------\n",
    "\n",
    "def _dist_mod(u, spacing):\n",
    "    \"\"\"Distance to nearest line center in a periodic family with pitch=spacing.\"\"\"\n",
    "    r = np.mod(u, spacing)\n",
    "    return np.minimum(r, spacing - r)\n",
    "\n",
    "def _rectilinear_mask(XX, YY, spacing, line_width, angle_deg=0.0, phase=0.0):\n",
    "    \"\"\"\n",
    "    Rectilinear (parallel lines) mask at angle_deg, with true line width.\n",
    "    Lines are centered where (x cos + y sin + phase) is a multiple of spacing.\n",
    "    \"\"\"\n",
    "    th = np.deg2rad(angle_deg)\n",
    "    # Coordinate along the line-normal direction (u-axis)\n",
    "    u =  XX * np.cos(th) + YY * np.sin(th)\n",
    "    dist = _dist_mod(u + phase, spacing)\n",
    "    # Render a line of width 'line_width' around the center => half width threshold\n",
    "    return dist <= (line_width / 2.0)\n",
    "\n",
    "def _spacing_for_grid_density(line_width, f):\n",
    "    \"\"\"\n",
    "    For a two-axis grid (two orthogonal families of rectilinear lines with true width 'w'),\n",
    "    the area fraction is f = 2r - r^2 where r = w/s.\n",
    "    Solve for s: r = 1 - sqrt(1 - f)  =>  s = w / r.\n",
    "    \"\"\"\n",
    "    f = float(np.clip(f, 0.0, 1.0))\n",
    "    if f <= 0.0:\n",
    "        return np.inf\n",
    "    if f >= 1.0:\n",
    "        return line_width  # centers are one line_width apart for a \"solid\" raster\n",
    "    r = 1.0 - np.sqrt(1.0 - f)\n",
    "    return line_width / r\n",
    "\n",
    "# ------------------------------\n",
    "# Area & inertia on a raster mask\n",
    "# ------------------------------\n",
    "\n",
    "def compute_area_moments(XX, YY, mask):\n",
    "    x_vals = XX[mask]\n",
    "    y_vals = YY[mask]\n",
    "\n",
    "    if x_vals.size == 0:\n",
    "        return {\"A\": 0.0, \"x_c\": 0.0, \"y_c\": 0.0, \"I_x\": 0.0, \"I_y\": 0.0, \"I_xy\": 0.0, \"J\": 0.0}\n",
    "\n",
    "    dx = XX[0, 1] - XX[0, 0]\n",
    "    dy = YY[1, 0] - YY[0, 0]\n",
    "    dA = dx * dy\n",
    "\n",
    "    A   = x_vals.size * dA\n",
    "    x_c = float(np.mean(x_vals))\n",
    "    y_c = float(np.mean(y_vals))\n",
    "\n",
    "    x_shift = x_vals - x_c\n",
    "    y_shift = y_vals - y_c\n",
    "\n",
    "    I_x = float(np.sum(y_shift**2) * dA)\n",
    "    I_y = float(np.sum(x_shift**2) * dA)\n",
    "    I_xy = float(np.sum(x_shift * y_shift) * dA)\n",
    "    J   = I_x + I_y\n",
    "\n",
    "    return {\"A\": A, \"x_c\": x_c, \"y_c\": y_c, \"I_x\": I_x, \"I_y\": I_y, \"I_xy\": I_xy, \"J\": J}\n",
    "\n",
    "# ------------------------------\n",
    "# Main: Prusa-style rectilinear/grid with shells\n",
    "# ------------------------------\n",
    "\n",
    "def prusa_style_grid_or_rectilinear(\n",
    "    width, height,\n",
    "    infill_fraction,\n",
    "    line_width=0.42,\n",
    "    shell_layers=2,\n",
    "    base_infill_angle_deg=45.0,\n",
    "    grid=True,\n",
    "    z_height=0.0,              # keep for optional phase control\n",
    "    phase_per_mm=0.0,\n",
    "    # --- NEW: cross-sections extruded through Z ---\n",
    "    z_object_height=None,      # mm (required to report XZ / YZ areas)\n",
    "    y_plane=0.0,               # mm, plane parallel to XZ at y = y_plane\n",
    "    x_plane=0.0,               # mm, plane parallel to YZ at x = x_plane\n",
    "    N=800,\n",
    "    plot=True,\n",
    "    verbose=True\n",
    "):\n",
    "    # ----- Raster grid\n",
    "    xs = np.linspace(-width/2,  width/2,  N)\n",
    "    ys = np.linspace(-height/2, height/2, N)\n",
    "    XX, YY = np.meshgrid(xs, ys)\n",
    "\n",
    "    # ----- Shells (perimeters)\n",
    "    shell_mask = np.zeros_like(XX, dtype=bool)\n",
    "    for i in range(shell_layers):\n",
    "        off = (i + 0.5) * line_width\n",
    "        shell_mask |= np.abs(XX - (-width/2  + off)) <= (line_width / 2)\n",
    "        shell_mask |= np.abs(XX - ( +width/2  - off)) <= (line_width / 2)\n",
    "        shell_mask |= np.abs(YY - (-height/2 + off)) <= (line_width / 2)\n",
    "        shell_mask |= np.abs(YY - ( +height/2 - off)) <= (line_width / 2)\n",
    "\n",
    "    # ----- Infill region (inside shells)\n",
    "    inner_offset = shell_layers * line_width\n",
    "    inner_rect = (\n",
    "        (np.abs(XX) <= (width/2  - inner_offset)) &\n",
    "        (np.abs(YY) <= (height/2 - inner_offset))\n",
    "    )\n",
    "\n",
    "    if infill_fraction <= 0.0:\n",
    "        infill_mask = np.zeros_like(XX, dtype=bool)\n",
    "    elif infill_fraction >= 1.0:\n",
    "        spacing = line_width\n",
    "        masks = []\n",
    "        angles = [base_infill_angle_deg] + ([base_infill_angle_deg + 90] if grid else [])\n",
    "        phase = phase_per_mm * z_height\n",
    "        for a in angles:\n",
    "            masks.append(_rectilinear_mask(XX, YY, spacing, line_width, angle_deg=a, phase=phase))\n",
    "        infill_mask = np.logical_or.reduce(masks) & inner_rect\n",
    "    else:\n",
    "        spacing = _spacing_for_grid_density(line_width, infill_fraction) if grid \\\n",
    "                  else line_width / infill_fraction\n",
    "        masks = []\n",
    "        angles = [base_infill_angle_deg] + ([base_infill_angle_deg + 90] if grid else [])\n",
    "        phase = phase_per_mm * z_height\n",
    "        for a in angles:\n",
    "            masks.append(_rectilinear_mask(XX, YY, spacing, line_width, angle_deg=a, phase=phase))\n",
    "        infill_mask = np.logical_or.reduce(masks) & inner_rect\n",
    "\n",
    "    # ----- Final mask (material present)\n",
    "    final_mask = shell_mask | infill_mask\n",
    "\n",
    "    # ----- Plot\n",
    "    if plot:\n",
    "        plt.figure(figsize=(6, 6))\n",
    "        img = np.where(final_mask, 1.0, np.nan)\n",
    "        plt.imshow(img, origin='lower',\n",
    "                   extent=[-width/2, width/2, -height/2, height/2],\n",
    "                   interpolation='nearest')\n",
    "        title = \"Grid\" if grid else \"Rectilinear\"\n",
    "        plt.title(f\"{title} @ {infill_fraction*100:.1f}%  | shells={shell_layers}×{line_width:.2f} angle={base_infill_angle_deg:.0f}°\")\n",
    "        plt.xlabel(\"X (mm)\")\n",
    "        plt.ylabel(\"Y (mm)\")\n",
    "        plt.gca().set_aspect('equal', 'box')\n",
    "        plt.grid(True)\n",
    "\n",
    "        # guide lines\n",
    "        plt.hlines(y_plane, -width/2, width/2, linestyles='--')\n",
    "        plt.vlines(x_plane, -height/2, height/2, linestyles='--')\n",
    "\n",
    "        plt.show()\n",
    "\n",
    "    # ----- XY numbers\n",
    "    total = compute_area_moments(XX, YY, final_mask)\n",
    "    shells = compute_area_moments(XX, YY, shell_mask)\n",
    "    A_infill = total[\"A\"] - shells[\"A\"]\n",
    "\n",
    "    # ----- NEW: cross-sections parallel to XZ and YZ\n",
    "    dx = XX[0, 1] - XX[0, 0]\n",
    "    dy = YY[1, 0] - YY[0, 0]\n",
    "\n",
    "    ys = YY[:, 0]\n",
    "    xs = XX[0, :]\n",
    "    row = int(np.argmin(np.abs(ys - y_plane)))\n",
    "    col = int(np.argmin(np.abs(xs - x_plane)))\n",
    "\n",
    "    length_x_at_y = float(np.count_nonzero(final_mask[row, :]) * dx)\n",
    "    length_y_at_x = float(np.count_nonzero(final_mask[:, col]) * dy)\n",
    "\n",
    "    area_xz_at_y = None\n",
    "    area_yz_at_x = None\n",
    "    if z_object_height is not None and z_object_height > 0:\n",
    "        area_xz_at_y = length_x_at_y * z_object_height\n",
    "        area_yz_at_x = length_y_at_x * z_object_height\n",
    "\n",
    "    if verbose:\n",
    "        print(f\"==== {('Grid' if grid else 'Rectilinear')} Infill {infill_fraction*100:.1f}% ====\")\n",
    "        print(f\"XY Total Area         = {total['A']:.4f} mm²\")\n",
    "        print(f\"  Shell Area          = {shells['A']:.4f} mm²\")\n",
    "        print(f\"  Infill Area         = {A_infill:.4f} mm²\")\n",
    "        print(f\"I_x                   = {total['I_x']:.4f}\")\n",
    "        print(f\"I_y                   = {total['I_y']:.4f}\")\n",
    "        print(f\"I_xy                  = {total['I_xy']:.4f}\")\n",
    "        print(f\"Polar moment, J       = {total['J']:.4f}\")\n",
    "        print(f\"-- Cross-sections through Z (pattern constant over Z) --\")\n",
    "        print(f\"Length along X @ y={y_plane:.3f} mm: {length_x_at_y:.4f} mm\")\n",
    "        print(f\"Length along Y @ x={x_plane:.3f} mm: {length_y_at_x:.4f} mm\")\n",
    "        if area_xz_at_y is not None:\n",
    "            print(f\"Area of XZ plane @ y={y_plane:.3f}: {area_xz_at_y:.4f} mm²  (Z={z_object_height:.3f} mm)\")\n",
    "        if area_yz_at_x is not None:\n",
    "            print(f\"Area of YZ plane @ x={x_plane:.3f}: {area_yz_at_x:.4f} mm²  (Z={z_object_height:.3f} mm)\")\n",
    "        print()\n",
    "\n",
    "    return {\n",
    "        \"mask\": final_mask,\n",
    "        \"XX\": XX, \"YY\": YY,\n",
    "        \"dx\": dx, \"dy\": dy,\n",
    "        \"area_total_xy\": total[\"A\"],\n",
    "        \"area_shells_xy\": shells[\"A\"],\n",
    "        \"area_infill_xy\": A_infill,\n",
    "        \"moments_xy\": total,\n",
    "        \"length_x_at_y\": length_x_at_y,\n",
    "        \"length_y_at_x\": length_y_at_x,\n",
    "        \"area_xz_at_y\": area_xz_at_y,\n",
    "        \"area_yz_at_x\": area_yz_at_x,\n",
    "    }\n",
    "\n",
    "# ------------------------------\n",
    "# Demo\n",
    "# ------------------------------\n",
    "if __name__ == \"__main__\":\n",
    "    W, H = 10.0, 10.0\n",
    "    Z = 20.0  # object height in Z\n",
    "    res = prusa_style_grid_or_rectilinear(\n",
    "        width=W, height=H,\n",
    "        infill_fraction=0.8,\n",
    "        line_width=0.42,\n",
    "        shell_layers=2,\n",
    "        base_infill_angle_deg=45.0,\n",
    "        grid=True,\n",
    "        z_object_height=Z,  # << set Z height\n",
    "        y_plane=+1.0,       # XZ cross-section at y = +1 mm\n",
    "        x_plane=-2.0,       # YZ cross-section at x = -2 mm\n",
    "        plot=True, verbose=True\n",
    "    )\n",
    "\n",
    "    print(\"A_xz(y=1mm) =\", res[\"area_xz_at_y\"], \"mm^2\")\n",
    "    print(\"A_yz(x=-2mm) =\", res[\"area_yz_at_x\"], \"mm^2\")\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "c04da43a-0894-4100-9457-0160dd2b54d2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 34144 characters]",
      "text/plain": [
       "<Figure size 600x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 39968 characters]",
      "text/plain": [
       "<Figure size 700x350 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 39256 characters]",
      "text/plain": [
       "<Figure size 700x350 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==== Grid Infill 30.0% ====\n",
      "XY Total Area         = 52.1754 mm²\n",
      "  Shell Area          = 31.1879 mm²\n",
      "  Infill Area         = 20.9874 mm²\n",
      "I_x                   = 567.1050\n",
      "I_y                   = 567.1050\n",
      "I_xy                  = 0.0000\n",
      "Polar moment, J       = 1134.2101\n",
      "-- Cross-sections through Z (pattern constant over Z) --\n",
      "Length along X @ y=1.000 mm: 4.0801 mm\n",
      "Length along Y @ x=-2.000 mm: 3.6045 mm\n",
      "Area of XZ plane @ y=1.000: 40.8010 mm²  (Z=10.000 mm)\n",
      "Area of YZ plane @ x=-2.000: 36.0451 mm²  (Z=10.000 mm)\n",
      "\n",
      "A_xz(y=1mm) = 40.80100125156456 mm^2\n",
      "A_yz(x=-2mm) = 36.04505632040059 mm^2\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# ------------------------------\n",
    "# Geometry utilities\n",
    "# ------------------------------\n",
    "\n",
    "def _dist_mod(u, spacing):\n",
    "    r = np.mod(u, spacing)\n",
    "    return np.minimum(r, spacing - r)\n",
    "\n",
    "def _rectilinear_mask(XX, YY, spacing, line_width, angle_deg=0.0, phase=0.0):\n",
    "    th = np.deg2rad(angle_deg)\n",
    "    u =  XX * np.cos(th) + YY * np.sin(th)\n",
    "    dist = _dist_mod(u + phase, spacing)\n",
    "    return dist <= (line_width / 2.0)\n",
    "\n",
    "def _spacing_for_grid_density(line_width, f):\n",
    "    f = float(np.clip(f, 0.0, 1.0))\n",
    "    if f <= 0.0:\n",
    "        return np.inf\n",
    "    if f >= 1.0:\n",
    "        return line_width\n",
    "    r = 1.0 - np.sqrt(1.0 - f)\n",
    "    return line_width / r\n",
    "\n",
    "# ------------------------------\n",
    "# Area & inertia on a raster mask\n",
    "# ------------------------------\n",
    "\n",
    "def compute_area_moments(XX, YY, mask):\n",
    "    x_vals = XX[mask]\n",
    "    y_vals = YY[mask]\n",
    "\n",
    "    if x_vals.size == 0:\n",
    "        return {\"A\": 0.0, \"x_c\": 0.0, \"y_c\": 0.0, \"I_x\": 0.0, \"I_y\": 0.0, \"I_xy\": 0.0, \"J\": 0.0}\n",
    "\n",
    "    dx = XX[0, 1] - XX[0, 0]\n",
    "    dy = YY[1, 0] - YY[0, 0]\n",
    "    dA = dx * dy\n",
    "\n",
    "    A   = x_vals.size * dA\n",
    "    x_c = float(np.mean(x_vals))\n",
    "    y_c = float(np.mean(y_vals))\n",
    "\n",
    "    x_shift = x_vals - x_c\n",
    "    y_shift = y_vals - y_c\n",
    "\n",
    "    I_x = float(np.sum(y_shift**2) * dA)\n",
    "    I_y = float(np.sum(x_shift**2) * dA)\n",
    "    I_xy = float(np.sum(x_shift * y_shift) * dA)\n",
    "    J   = I_x + I_y\n",
    "\n",
    "    return {\"A\": A, \"x_c\": x_c, \"y_c\": y_c, \"I_x\": I_x, \"I_y\": I_y, \"I_xy\": I_xy, \"J\": J}\n",
    "\n",
    "# ------------------------------\n",
    "# Main: Prusa-style rectilinear/grid with shells\n",
    "# ------------------------------\n",
    "\n",
    "def prusa_style_grid_or_rectilinear(\n",
    "    width, height,\n",
    "    infill_fraction,\n",
    "    line_width=0.42,\n",
    "    shell_layers=2,\n",
    "    base_infill_angle_deg=45.0,\n",
    "    grid=True,\n",
    "    z_height=0.0,              # optional phase control\n",
    "    phase_per_mm=0.0,\n",
    "    # Cross-sections extruded through Z\n",
    "    z_object_height=None,      # mm (if set => areas in mm²; else lengths in mm)\n",
    "    y_plane=0.0,               # mm, plane parallel to XZ at y = y_plane\n",
    "    x_plane=0.0,               # mm, plane parallel to YZ at x = x_plane\n",
    "    N=800,\n",
    "    plot=True,\n",
    "    verbose=True\n",
    "):\n",
    "    # ----- Raster grid (centered at origin)\n",
    "    xs = np.linspace(-width/2,  width/2,  N)\n",
    "    ys = np.linspace(-height/2, height/2, N)\n",
    "    XX, YY = np.meshgrid(xs, ys)\n",
    "\n",
    "    # ----- Shells (perimeters)\n",
    "    shell_mask = np.zeros_like(XX, dtype=bool)\n",
    "    for i in range(shell_layers):\n",
    "        off = (i + 0.5) * line_width\n",
    "        shell_mask |= np.abs(XX - (-width/2  + off)) <= (line_width / 2)\n",
    "        shell_mask |= np.abs(XX - ( +width/2  - off)) <= (line_width / 2)\n",
    "        shell_mask |= np.abs(YY - (-height/2 + off)) <= (line_width / 2)\n",
    "        shell_mask |= np.abs(YY - ( +height/2 - off)) <= (line_width / 2)\n",
    "\n",
    "    # ----- Infill region (inside shells)\n",
    "    inner_offset = shell_layers * line_width\n",
    "    inner_rect = (\n",
    "        (np.abs(XX) <= (width/2  - inner_offset)) &\n",
    "        (np.abs(YY) <= (height/2 - inner_offset))\n",
    "    )\n",
    "\n",
    "    if infill_fraction <= 0.0:\n",
    "        infill_mask = np.zeros_like(XX, dtype=bool)\n",
    "    elif infill_fraction >= 1.0:\n",
    "        spacing = line_width\n",
    "        masks = []\n",
    "        angles = [base_infill_angle_deg] + ([base_infill_angle_deg + 90] if grid else [])\n",
    "        phase = phase_per_mm * z_height\n",
    "        for a in angles:\n",
    "            masks.append(_rectilinear_mask(XX, YY, spacing, line_width, angle_deg=a, phase=phase))\n",
    "        infill_mask = np.logical_or.reduce(masks) & inner_rect\n",
    "    else:\n",
    "        spacing = _spacing_for_grid_density(line_width, infill_fraction) if grid \\\n",
    "                  else line_width / infill_fraction\n",
    "        masks = []\n",
    "        angles = [base_infill_angle_deg] + ([base_infill_angle_deg + 90] if grid else [])\n",
    "        phase = phase_per_mm * z_height\n",
    "        for a in angles:\n",
    "            masks.append(_rectilinear_mask(XX, YY, spacing, line_width, angle_deg=a, phase=phase))\n",
    "        infill_mask = np.logical_or.reduce(masks) & inner_rect\n",
    "\n",
    "    # ----- Final mask (material present)\n",
    "    final_mask = shell_mask | infill_mask\n",
    "\n",
    "    # ----- Plot: XY bitmap preview\n",
    "    if plot:\n",
    "        plt.figure(figsize=(6, 6))\n",
    "        img = np.where(final_mask, 1.0, np.nan)\n",
    "        plt.imshow(img, origin='lower',\n",
    "                   extent=[-width/2, width/2, -height/2, height/2],\n",
    "                   interpolation='nearest')\n",
    "        title = \"Grid\" if grid else \"Rectilinear\"\n",
    "        plt.title(f\"{title} @ {infill_fraction*100:.1f}%  | shells={shell_layers}×{line_width:.2f} angle={base_infill_angle_deg:.0f}°\")\n",
    "        plt.xlabel(\"X (mm)\")\n",
    "        plt.ylabel(\"Y (mm)\")\n",
    "        plt.gca().set_aspect('equal', 'box')\n",
    "        plt.grid(True)\n",
    "\n",
    "        # guide lines\n",
    "        plt.hlines(y_plane, -width/2, width/2, linestyles='--')\n",
    "        plt.vlines(x_plane, -height/2, height/2, linestyles='--')\n",
    "\n",
    "        plt.show()\n",
    "\n",
    "    # ----- XY numbers\n",
    "    total = compute_area_moments(XX, YY, final_mask)\n",
    "    shells = compute_area_moments(XX, YY, shell_mask)\n",
    "    A_infill = total[\"A\"] - shells[\"A\"]\n",
    "\n",
    "    # ----- Cross-sections parallel to XZ and YZ at specific planes\n",
    "    dx = XX[0, 1] - XX[0, 0]\n",
    "    dy = YY[1, 0] - YY[0, 0]\n",
    "    ys_centered = YY[:, 0]\n",
    "    xs_centered = XX[0, :]\n",
    "    row = int(np.argmin(np.abs(ys_centered - y_plane)))\n",
    "    col = int(np.argmin(np.abs(xs_centered - x_plane)))\n",
    "\n",
    "    length_x_at_y = float(np.count_nonzero(final_mask[row, :]) * dx)\n",
    "    length_y_at_x = float(np.count_nonzero(final_mask[:, col]) * dy)\n",
    "\n",
    "    area_xz_at_y = None\n",
    "    area_yz_at_x = None\n",
    "    if z_object_height is not None and z_object_height > 0:\n",
    "        area_xz_at_y = length_x_at_y * z_object_height\n",
    "        area_yz_at_x = length_y_at_x * z_object_height\n",
    "\n",
    "    # ----- NEW: Variation curves over full width/height\n",
    "    # lengths per row/column\n",
    "    length_x_vs_y = np.count_nonzero(final_mask, axis=1) * dx   # for each y-row, length along X\n",
    "    length_y_vs_x = np.count_nonzero(final_mask, axis=0) * dy   # for each x-col, length along Y\n",
    "\n",
    "    if z_object_height is not None and z_object_height > 0:\n",
    "        area_xz_vs_y = length_x_vs_y * z_object_height\n",
    "        area_yz_vs_x = length_y_vs_x * z_object_height\n",
    "        y_label_curves = \"Area of XZ slice (mm²)\"\n",
    "        x_label_curves = \"Area of YZ slice (mm²)\"\n",
    "    else:\n",
    "        area_xz_vs_y = length_x_vs_y\n",
    "        area_yz_vs_x = length_y_vs_x\n",
    "        y_label_curves = \"Length along X (mm)  [set z_object_height for area]\"\n",
    "        x_label_curves = \"Length along Y (mm)  [set z_object_height for area]\"\n",
    "\n",
    "    # axes remapped to start at 0 (wall) → up to width/height\n",
    "    y_axis_0_to_H = ys_centered + height/2.0\n",
    "    x_axis_0_to_W = xs_centered + width/2.0\n",
    "\n",
    "    if plot:\n",
    "        # --- Area vs y (XZ plane area as y varies)\n",
    "        plt.figure(figsize=(7, 3.5))\n",
    "        plt.plot(y_axis_0_to_H, area_xz_vs_y)\n",
    "        plt.xlabel(\"y from bottom wall (mm)\")\n",
    "        plt.ylabel(y_label_curves)\n",
    "        plt.title(\"Variation vs y\")\n",
    "        plt.grid(True)\n",
    "        plt.xlim(0, height)\n",
    "        plt.tight_layout()\n",
    "        plt.show()\n",
    "\n",
    "        # --- Area vs x (YZ plane area as x varies)\n",
    "        plt.figure(figsize=(7, 3.5))\n",
    "        plt.plot(x_axis_0_to_W, area_yz_vs_x)\n",
    "        plt.xlabel(\"x from left wall (mm)\")\n",
    "        plt.ylabel(x_label_curves)\n",
    "        plt.title(\"Variation vs x\")\n",
    "        plt.grid(True)\n",
    "        plt.xlim(0, width)\n",
    "        plt.tight_layout()\n",
    "        plt.show()\n",
    "\n",
    "    if verbose:\n",
    "        print(f\"==== {('Grid' if grid else 'Rectilinear')} Infill {infill_fraction*100:.1f}% ====\")\n",
    "        print(f\"XY Total Area         = {total['A']:.4f} mm²\")\n",
    "        print(f\"  Shell Area          = {shells['A']:.4f} mm²\")\n",
    "        print(f\"  Infill Area         = {A_infill:.4f} mm²\")\n",
    "        print(f\"I_x                   = {total['I_x']:.4f}\")\n",
    "        print(f\"I_y                   = {total['I_y']:.4f}\")\n",
    "        print(f\"I_xy                  = {total['I_xy']:.4f}\")\n",
    "        print(f\"Polar moment, J       = {total['J']:.4f}\")\n",
    "        print(f\"-- Cross-sections through Z (pattern constant over Z) --\")\n",
    "        print(f\"Length along X @ y={y_plane:.3f} mm: {length_x_at_y:.4f} mm\")\n",
    "        print(f\"Length along Y @ x={x_plane:.3f} mm: {length_y_at_x:.4f} mm\")\n",
    "        if area_xz_at_y is not None:\n",
    "            print(f\"Area of XZ plane @ y={y_plane:.3f}: {area_xz_at_y:.4f} mm²  (Z={z_object_height:.3f} mm)\")\n",
    "        if area_yz_at_x is not None:\n",
    "            print(f\"Area of YZ plane @ x={x_plane:.3f}: {area_yz_at_x:.4f} mm²  (Z={z_object_height:.3f} mm)\")\n",
    "        print()\n",
    "\n",
    "    return {\n",
    "        \"mask\": final_mask,\n",
    "        \"XX\": XX, \"YY\": YY,\n",
    "        \"dx\": dx, \"dy\": dy,\n",
    "        \"area_total_xy\": total[\"A\"],\n",
    "        \"area_shells_xy\": shells[\"A\"],\n",
    "        \"area_infill_xy\": A_infill,\n",
    "        \"moments_xy\": total,\n",
    "        \"length_x_at_y\": length_x_at_y,\n",
    "        \"length_y_at_x\": length_y_at_x,\n",
    "        \"area_xz_at_y\": area_xz_at_y,\n",
    "        \"area_yz_at_x\": area_yz_at_x,\n",
    "        # NEW: full variation arrays and their axes (0→W/H)\n",
    "        \"y_axis_mm\": y_axis_0_to_H,\n",
    "        \"x_axis_mm\": x_axis_0_to_W,\n",
    "        \"area_xz_vs_y\": area_xz_vs_y,\n",
    "        \"area_yz_vs_x\": area_yz_vs_x,\n",
    "    }\n",
    "\n",
    "# ------------------------------\n",
    "# Demo\n",
    "# ------------------------------\n",
    "if __name__ == \"__main__\":\n",
    "    W, H = 10.0, 10.0\n",
    "    Z = 10.0  # object height in Z\n",
    "    res = prusa_style_grid_or_rectilinear(\n",
    "        width=W, height=H,\n",
    "        infill_fraction=0.3,\n",
    "        line_width=0.42,\n",
    "        shell_layers=2,\n",
    "        base_infill_angle_deg=45.0,\n",
    "        grid=True,\n",
    "        z_object_height=Z,  # set Z height for true areas\n",
    "        y_plane=+1.0,       # XZ cross-section at y = +1 mm\n",
    "        x_plane=-2.0,       # YZ cross-section at x = -2 mm (centered coords)\n",
    "        N=800,\n",
    "        plot=True, verbose=True\n",
    "    )\n",
    "\n",
    "    print(\"A_xz(y=1mm) =\", res[\"area_xz_at_y\"], \"mm^2\")\n",
    "    print(\"A_yz(x=-2mm) =\", res[\"area_yz_at_x\"], \"mm^2\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "adbb08c3-31f7-4b32-b37b-f6fde39033fd",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 30276 characters]",
      "text/plain": [
       "<Figure size 600x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 48324 characters]",
      "text/plain": [
       "<Figure size 700x350 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 37020 characters]",
      "text/plain": [
       "<Figure size 700x350 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==== Grid ispuna 20.0% ====\n",
      "XY ukupna povrsina     = 245.4207 mm²\n",
      "  Povrsina ljuski      = 132.4033 mm²\n",
      "  Povrsina ispune      = 113.0174 mm²\n",
      "-- Presjeci kroz Z (uzorak konstantan po Z) --\n",
      "Duzina po X @ y=1.000 mm: 2.8786 mm\n",
      "Duzina po Y @ x=-2.000 mm: 15.9449 mm\n",
      "Povrsina XZ @ y=1.000: 28.7860 mm²  (Z=10.000 mm)\n",
      "Povrsina YZ @ x=-2.000: 159.4493 mm²  (Z=10.000 mm)\n",
      "\n",
      "A_xz(y=1mm) = 28.785982478097694 mm^2\n",
      "A_yz(x=-2mm) = 159.44931163954337 mm^2\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# ------------------------------\n",
    "# Geometrijske pomocne funkcije\n",
    "# ------------------------------\n",
    "\n",
    "def _udaljenost_mod(u, razmak):\n",
    "    \"\"\"Najmanja udaljenost do najblize paralelne linije s periodom 'razmak'.\"\"\"\n",
    "    r = np.mod(u, razmak)\n",
    "    return np.minimum(r, razmak - r)\n",
    "\n",
    "def _pravocrtna_maska(XX, YY, razmak, sirina_linije, kut_stupnjevi=0.0, faza=0.0):\n",
    "    \"\"\"\n",
    "    Generira pravocrtni uzorak (paralelne linije) kao booleovu masku.\n",
    "    XX, YY: mreza koordinata; razmak: period linija; sirina_linije: debljina trake.\n",
    "    kut_stupnjevi: orijentacija linija; faza: pomak uzorka.\n",
    "    \"\"\"\n",
    "    th = np.deg2rad(kut_stupnjevi)\n",
    "    u =  XX * np.cos(th) + YY * np.sin(th)\n",
    "    dist = _udaljenost_mod(u + faza, razmak)\n",
    "    return dist <= (sirina_linije / 2.0)\n",
    "\n",
    "def _razmak_za_gustocu_mreze(sirina_linije, f):\n",
    "    \"\"\"\n",
    "    Za zadani udio ispune f (0..1) vrati razmak linija koji daje istu gustocu\n",
    "    kod 'grid' (dvije orijentacije pod 90°). Za cistu pravocrtnu (jedna orijentacija)\n",
    "    koristi se sirina_linije / f (vidi dolje).\n",
    "    \"\"\"\n",
    "    f = float(np.clip(f, 0.0, 1.0))\n",
    "    if f <= 0.0:\n",
    "        return np.inf\n",
    "    if f >= 1.0:\n",
    "        return sirina_linije\n",
    "    r = 1.0 - np.sqrt(1.0 - f)\n",
    "    return sirina_linije / r\n",
    "\n",
    "# ------------------------------\n",
    "# Povrsina i momenti na raster maski\n",
    "# ------------------------------\n",
    "\n",
    "def izracun_povrsine_i_momenata(XX, YY, maska):\n",
    "    \"\"\"\n",
    "    Racuna povrsinu A, te geometrijske momente inercije oko tezista (I_x, I_y, I_xy)\n",
    "    na raster reprezentaciji (True piksli su 'materijal').\n",
    "    \"\"\"\n",
    "    x_vals = XX[maska]\n",
    "    y_vals = YY[maska]\n",
    "\n",
    "    if x_vals.size == 0:\n",
    "        return {\"A\": 0.0, \"x_c\": 0.0, \"y_c\": 0.0, \"I_x\": 0.0, \"I_y\": 0.0, \"I_xy\": 0.0, \"J\": 0.0}\n",
    "\n",
    "    dx = XX[0, 1] - XX[0, 0]\n",
    "    dy = YY[1, 0] - YY[0, 0]\n",
    "    dA = dx * dy\n",
    "\n",
    "    A   = x_vals.size * dA\n",
    "    x_c = float(np.mean(x_vals))\n",
    "    y_c = float(np.mean(y_vals))\n",
    "\n",
    "    x_p = x_vals - x_c\n",
    "    y_p = y_vals - y_c\n",
    "\n",
    "    I_x = float(np.sum(y_p**2) * dA)\n",
    "    I_y = float(np.sum(x_p**2) * dA)\n",
    "    I_xy = float(np.sum(x_p * y_p) * dA)\n",
    "    J   = I_x + I_y\n",
    "\n",
    "    return {\"A\": A, \"x_c\": x_c, \"y_c\": y_c, \"I_x\": I_x, \"I_y\": I_y, \"I_xy\": I_xy, \"J\": J}\n",
    "\n",
    "# ------------------------------\n",
    "# Glavno: Prusa-stil pravocrtni/grid s ljuskama (perimetrima)\n",
    "# ------------------------------\n",
    "\n",
    "def prusa_mreza_ili_pravocrtna(\n",
    "    sirina, visina,\n",
    "    udio_ispune,\n",
    "    sirina_linije=0.42,\n",
    "    slojevi_ljuske=2,\n",
    "    osnovni_kut_ispune_stupnjevi=45.0,\n",
    "    mreza=True,\n",
    "    z_visina=0.0,         # opcionalna faza po Z\n",
    "    faza_po_mm=0.0,\n",
    "    # Poprecni presjeci kroz Z\n",
    "    z_visina_objekta=None,  # mm (ako je postavljeno -> povrsine u mm²; inace duzine u mm)\n",
    "    y_ravnina=0.0,          # mm, ravnina paralelna s XZ na y = y_ravnina\n",
    "    x_ravnina=0.0,          # mm, ravnina paralelna s YZ na x = x_ravnina\n",
    "    N=800,\n",
    "    graficki_prikaz=True,\n",
    "    detaljno=True\n",
    "):\n",
    "    # ----- Raster mreza (centar u ishodištu)\n",
    "    xs = np.linspace(-sirina/2,  sirina/2,  N)\n",
    "    ys = np.linspace(-visina/2, visina/2, N)\n",
    "    XX, YY = np.meshgrid(xs, ys)\n",
    "\n",
    "    # ----- Ljuske (perimetri)\n",
    "    shell_mask = np.zeros_like(XX, dtype=bool)\n",
    "    for i in range(slojevi_ljuske):\n",
    "        off = (i + 0.5) * sirina_linije\n",
    "        shell_mask |= np.abs(XX - (-sirina/2  + off)) <= (sirina_linije / 2)\n",
    "        shell_mask |= np.abs(XX - ( +sirina/2  - off)) <= (sirina_linije / 2)\n",
    "        shell_mask |= np.abs(YY - (-visina/2 + off)) <= (sirina_linije / 2)\n",
    "        shell_mask |= np.abs(YY - ( +visina/2 - off)) <= (sirina_linije / 2)\n",
    "\n",
    "    # ----- Unutarnji pravokutnik (podrucje ispune)\n",
    "    unutarnji_pomak = slojevi_ljuske * sirina_linije\n",
    "    unutarnji_pravokutnik = (\n",
    "        (np.abs(XX) <= (sirina/2  - unutarnji_pomak)) &\n",
    "        (np.abs(YY) <= (visina/2 - unutarnji_pomak))\n",
    "    )\n",
    "\n",
    "    # ----- Ispuna\n",
    "    if udio_ispune <= 0.0:\n",
    "        infill_mask = np.zeros_like(XX, dtype=bool)\n",
    "    elif udio_ispune >= 1.0:\n",
    "        razmak = sirina_linije\n",
    "        maske = []\n",
    "        kutevi = [osnovni_kut_ispune_stupnjevi] + ([osnovni_kut_ispune_stupnjevi + 90] if mreza else [])\n",
    "        faza = faza_po_mm * z_visina\n",
    "        for a in kutevi:\n",
    "            maske.append(_pravocrtna_maska(XX, YY, razmak, sirina_linije, kut_stupnjevi=a, faza=faza))\n",
    "        infill_mask = np.logical_or.reduce(maske) & unutarnji_pravokutnik\n",
    "    else:\n",
    "        razmak = _razmak_za_gustocu_mreze(sirina_linije, udio_ispune) if mreza \\\n",
    "                 else sirina_linije / udio_ispune\n",
    "        maske = []\n",
    "        kutevi = [osnovni_kut_ispune_stupnjevi] + ([osnovni_kut_ispune_stupnjevi + 90] if mreza else [])\n",
    "        faza = faza_po_mm * z_visina\n",
    "        for a in kutevi:\n",
    "            maske.append(_pravocrtna_maska(XX, YY, razmak, sirina_linije, kut_stupnjevi=a, faza=faza))\n",
    "        infill_mask = np.logical_or.reduce(maske) & unutarnji_pravokutnik\n",
    "\n",
    "    # ----- Konacna maska (materijal prisutan)\n",
    "    konacna_maska = shell_mask | infill_mask\n",
    "\n",
    "    # ----- Graficki pregled XY\n",
    "    if graficki_prikaz:\n",
    "        plt.figure(figsize=(6, 6))\n",
    "        img = np.where(konacna_maska, 1.0, np.nan)\n",
    "        plt.imshow(img, origin='lower',\n",
    "                   extent=[-sirina/2, sirina/2, -visina/2, visina/2],\n",
    "                   interpolation='nearest')\n",
    "        naslov = \"Grid\" if mreza else \"Pravocrtna\"\n",
    "        plt.title(f\"{naslov} @ {udio_ispune*100:.1f}%  | ljuske={slojevi_ljuske}×{sirina_linije:.2f} kut={osnovni_kut_ispune_stupnjevi:.0f}°\")\n",
    "        plt.xlabel(\"X (mm)\")\n",
    "        plt.ylabel(\"Y (mm)\")\n",
    "        plt.gca().set_aspect('equal', 'box')\n",
    "        plt.grid(True)\n",
    "        # vodilice\n",
    "        plt.hlines(y_ravnina, -sirina/2, sirina/2, linestyles='--')\n",
    "        plt.vlines(x_ravnina, -visina/2, visina/2, linestyles='--')\n",
    "        plt.show()\n",
    "\n",
    "    # ----- XY brojke\n",
    "    total = izracun_povrsine_i_momenata(XX, YY, konacna_maska)\n",
    "    ljuske = izracun_povrsine_i_momenata(XX, YY, shell_mask)\n",
    "    A_ispuna = total[\"A\"] - ljuske[\"A\"]\n",
    "\n",
    "    # ----- Presjeci paralelni s XZ i YZ kroz zadane ravnine\n",
    "    dx = XX[0, 1] - XX[0, 0]\n",
    "    dy = YY[1, 0] - YY[0, 0]\n",
    "    ys_centered = YY[:, 0]\n",
    "    xs_centered = XX[0, :]\n",
    "    row = int(np.argmin(np.abs(ys_centered - y_ravnina)))\n",
    "    col = int(np.argmin(np.abs(xs_centered - x_ravnina)))\n",
    "\n",
    "    duzina_x_na_y = float(np.count_nonzero(konacna_maska[row, :]) * dx)\n",
    "    duzina_y_na_x = float(np.count_nonzero(konacna_maska[:, col]) * dy)\n",
    "\n",
    "    povrsina_xz_na_y = None\n",
    "    povrsina_yz_na_x = None\n",
    "    if z_visina_objekta is not None and z_visina_objekta > 0:\n",
    "        povrsina_xz_na_y = duzina_x_na_y * z_visina_objekta\n",
    "        povrsina_yz_na_x = duzina_y_na_x * z_visina_objekta\n",
    "\n",
    "    # ----- Krivulje varijacije preko cijele visine/sirine\n",
    "    duzina_x_vs_y = np.count_nonzero(konacna_maska, axis=1) * dx\n",
    "    duzina_y_vs_x = np.count_nonzero(konacna_maska, axis=0) * dy\n",
    "\n",
    "    if z_visina_objekta is not None and z_visina_objekta > 0:\n",
    "        povrsina_xz_vs_y = duzina_x_vs_y * z_visina_objekta\n",
    "        povrsina_yz_vs_x = duzina_y_vs_x * z_visina_objekta\n",
    "        y_oznaka = \"Povrsina XZ presjeka (mm²)\"\n",
    "        x_oznaka = \"Povrsina YZ presjeka (mm²)\"\n",
    "    else:\n",
    "        povrsina_xz_vs_y = duzina_x_vs_y\n",
    "        povrsina_yz_vs_x = duzina_y_vs_x\n",
    "        y_oznaka = \"Duzina po X (mm)  [postavi z_visina_objekta za povrsinu]\"\n",
    "        x_oznaka = \"Duzina po Y (mm)  [postavi z_visina_objekta za povrsinu]\"\n",
    "\n",
    "    # osi od 0 (lijevi/donji zid) do sirina/visina\n",
    "    y_os_0_do_H = ys_centered + visina/2.0\n",
    "    x_os_0_do_W = xs_centered + sirina/2.0\n",
    "\n",
    "    if graficki_prikaz:\n",
    "        # --- Povrsina naspram y (XZ presjek kako y varira)\n",
    "        plt.figure(figsize=(7, 3.5))\n",
    "        plt.plot(y_os_0_do_H, povrsina_xz_vs_y)\n",
    "        plt.xlabel(\"y od donjeg zida (mm)\")\n",
    "        plt.ylabel(y_oznaka)\n",
    "        plt.title(\"Varijacija prema y\")\n",
    "        plt.grid(True)\n",
    "        plt.xlim(0, visina)\n",
    "        plt.tight_layout()\n",
    "        plt.show()\n",
    "\n",
    "        # --- Povrsina naspram x (YZ presjek kako x varira)\n",
    "        plt.figure(figsize=(7, 3.5))\n",
    "        plt.plot(x_os_0_do_W, povrsina_yz_vs_x)\n",
    "        plt.xlabel(\"x od lijevog zida (mm)\")\n",
    "        plt.ylabel(x_oznaka)\n",
    "        plt.title(\"Varijacija prema x\")\n",
    "        plt.grid(True)\n",
    "        plt.xlim(0, sirina)\n",
    "        plt.tight_layout()\n",
    "        plt.show()\n",
    "\n",
    "    if detaljno:\n",
    "        print(f\"==== {('Grid' if mreza else 'Pravocrtna')} ispuna {udio_ispune*100:.1f}% ====\")\n",
    "        print(f\"XY ukupna povrsina     = {total['A']:.4f} mm²\")\n",
    "        print(f\"  Povrsina ljuski      = {ljuske['A']:.4f} mm²\")\n",
    "        print(f\"  Povrsina ispune      = {A_ispuna:.4f} mm²\")\n",
    " #       print(f\"I_x                    = {total['I_x']:.4f}\")\n",
    " #       print(f\"I_y                    = {total['I_y']:.4f}\")\n",
    " #       print(f\"I_xy                   = {total['I_xy']:.4f}\")\n",
    " #       print(f\"Polarni moment, J      = {total['J']:.4f}\")\n",
    "        print(f\"-- Presjeci kroz Z (uzorak konstantan po Z) --\")\n",
    "        print(f\"Duzina po X @ y={y_ravnina:.3f} mm: {duzina_x_na_y:.4f} mm\")\n",
    "        print(f\"Duzina po Y @ x={x_ravnina:.3f} mm: {duzina_y_na_x:.4f} mm\")\n",
    "        if povrsina_xz_na_y is not None:\n",
    "            print(f\"Povrsina XZ @ y={y_ravnina:.3f}: {povrsina_xz_na_y:.4f} mm²  (Z={z_visina_objekta:.3f} mm)\")\n",
    "        if povrsina_yz_na_x is not None:\n",
    "            print(f\"Povrsina YZ @ x={x_ravnina:.3f}: {povrsina_yz_na_x:.4f} mm²  (Z={z_visina_objekta:.3f} mm)\")\n",
    "        print()\n",
    "\n",
    "    return {\n",
    "        \"maska\": konacna_maska,\n",
    "        \"XX\": XX, \"YY\": YY,\n",
    "        \"dx\": dx, \"dy\": dy,\n",
    "        \"povrsina_ukupno_xy\": total[\"A\"],\n",
    "        \"povrsina_ljuske_xy\": ljuske[\"A\"],\n",
    "        \"povrsina_ispune_xy\": A_ispuna,\n",
    "        \"momenti_xy\": total,\n",
    "        \"duzina_x_na_y\": duzina_x_na_y,\n",
    "        \"duzina_y_na_x\": duzina_y_na_x,\n",
    "        \"povrsina_xz_na_y\": povrsina_xz_na_y,\n",
    "        \"povrsina_yz_na_x\": povrsina_yz_na_x,\n",
    "        # pune krivulje varijacije i njihove osi (0→sirina/visina)\n",
    "        \"y_os_mm\": y_os_0_do_H,\n",
    "        \"x_os_mm\": x_os_0_do_W,\n",
    "        \"povrsina_xz_vs_y\": povrsina_xz_vs_y,\n",
    "        \"povrsina_yz_vs_x\": povrsina_yz_vs_x,\n",
    "    }\n",
    "\n",
    "# ------------------------------\n",
    "# Demo\n",
    "# ------------------------------\n",
    "if __name__ == \"__main__\":\n",
    "    W, H = 10.0, 70.0\n",
    "    Z = 10.0  # visina objekta u Z\n",
    "    res = prusa_mreza_ili_pravocrtna(\n",
    "        sirina=W, visina=H,\n",
    "        udio_ispune=0.2,\n",
    "        sirina_linije=0.42,\n",
    "        slojevi_ljuske=2,\n",
    "        osnovni_kut_ispune_stupnjevi=45.0,\n",
    "        mreza=True,\n",
    "        z_visina_objekta=Z,  # postavi Z za prave povrsine\n",
    "        y_ravnina=+1.0,      # XZ presjek na y = +1 mm\n",
    "        x_ravnina=-2.0,      # YZ presjek na x = -2 mm (cent. koord.)\n",
    "        N=800,\n",
    "        graficki_prikaz=True, detaljno=True\n",
    "    )\n",
    "\n",
    "    print(\"A_xz(y=1mm) =\", res[\"povrsina_xz_na_y\"], \"mm^2\")\n",
    "    print(\"A_yz(x=-2mm) =\", res[\"povrsina_yz_na_x\"], \"mm^2\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "60473fab-724f-43a9-919b-09c571734009",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2363b1b8-e9f0-47f5-b74b-4827e3ab6f91",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
