This commit is contained in:
2026-03-19 14:54:09 +03:00
parent 7145b66e0e
commit 37036f8f75
26 changed files with 5466 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View File

@@ -0,0 +1,35 @@
@startuml
title Canvas/ViewPort: алгоритм вычисления видимой области и редроу
start
:Get zoomed document rect\n(getZoomedImageSize);
:Read viewport rect + scroll offset;
:Compute visible width/height\nmin(viewport, image size);
:Compute raw visible x/y\nfrom scroll - image offset;
:Clamp x/y into image bounds;
:Store Rect_i{x,y,w,h} as _visible_rect;
if (visible rect changed\nOR texture is null?) then (yes)
:request redraw;
else (no)
:no redraw needed;
endif
if (redraw pending?) then (yes)
:Read render quality %;
:Convert area-quality to side-scale\nscale = sqrt(q/100);
:Scale full canvas size;
:Scale visible rect;
:Clamp scaled rect to scaled canvas;
if (scaled visible rect valid?) then (yes)
:Render only scaled visible area\nthrough RenderEngine;
:Update texture + stats + throttle;
else (no)
:Drop texture / skip rendering;
endif
else (no)
:Keep previous frame;
endif
stop
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

View File

@@ -0,0 +1,28 @@
@startuml
title Конвейер координат: Local -> World -> Canvas -> Buffer
skinparam rectangle {
RoundCorner 10
}
rectangle "Local coords\n(object space)" as L
rectangle "World coords\n(document space)" as W
rectangle "Canvas coords\n(scaled document)" as C
rectangle "Buffer coords\n(visible rect origin)" as B
L --> W : localToWorld()\nrotate + scale + translate
W --> C : world * scale_x/scale_y
C --> B : subtract visible_rect.(x,y)
B --> C : + visible_rect.(x,y)
C --> W : divide by scale_x/scale_y
W --> L : worldToLocal()\ntranslate^-1 + rotate^-1 + scale^-1
note right of B
All raster tests are done
in buffer coords (integer pixels).
Shape analytics often done
in local coords after inverse map.
end note
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@@ -0,0 +1,43 @@
@startuml
title Эллипс/дуга: алгоритм растризации
start
:Read rx, ry, thickness,\narc_percent, closed, filled;
if (rx<=0 or ry<=0?) then (yes)
stop
endif
:Build local bbox corners with margin;
:Transform corners to buffer\nand compute pixel scan bounds;
:Create temporary transparent buffer;
if (do_fill?) then (yes)
:startFill() collect border pixels;
endif
while (for each pixel in bbox)
:Map pixel center Buffer -> World -> Local;
:nx = x/rx, ny = y/ry,\nd = nx^2 + ny^2;
if (d in stroke ring?) then (yes)
if (arc_percent < 100?) then (yes)
:Compute angular position\nvia atan2 and normalized diff;
if (inside arc sector?) then (yes)
:plot stroke pixel;
endif
else (full ellipse)
:plot stroke pixel;
endif
endif
endwhile
if (closed and arc_percent<100?) then (yes)
:Draw two radial segments\n(center->start and center->end);
endif
if (do_fill?) then (yes)
:stopFill(fill_color);
endif
:Composite temp buffer to target\nonce with object opacity;
stop
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View File

@@ -0,0 +1,34 @@
@startuml
title Liang-Barsky: отсечение отрезка прямоугольником
start
:Input segment P0,P1 and clip rect;
:dx = x1-x0, dy = y1-y0;
:t0 = 0, t1 = 1;
:Process boundary x >= left\np=-dx, q=x0-left;
if (clip test fails?) then (yes)
stop
endif
:Process boundary x <= right\np=dx, q=right-x0;
if (clip test fails?) then (yes)
stop
endif
:Process boundary y >= top\np=-dy, q=y0-top;
if (clip test fails?) then (yes)
stop
endif
:Process boundary y <= bottom\np=dy, q=bottom-y0;
if (clip test fails?) then (yes)
stop
endif
:Compute clipped points:\nP0' = P0 + t0*(P1-P0)\nP1' = P0 + t1*(P1-P0);
:Round to integer pixels;
:Return accepted clipped segment;
stop
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

View File

@@ -0,0 +1,30 @@
@startuml
title Линия: полная схема растризации
start
:Input local endpoints (x0,y0),(x1,y1)\n+ color + thickness;
:Map endpoints Local -> World -> Buffer;
:Compute pixel thickness from transform scale;
if (thickness_px <= 0?) then (yes)
stop
endif
if (draw_when_outside == false?) then (yes)
:Clip segment to expanded buffer bounds\n(Liang-Barsky);
if (segment rejected?) then (yes)
stop
endif
endif
:Compute angle-based thickness correction\n(using |dx|/len and |dy|/len);
:Initialize Bresenham state\ndx,dy,sx,sy,err;
while (not reached end point?)
:Choose effective half-thickness:\nfull in viewport, 0 outside if draw_when_outside;
:Draw stripe around center pixel\n(along normal axis);
:Advance Bresenham step by error update;
endwhile
stop
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View File

@@ -0,0 +1,27 @@
@startuml
title PMA alpha blending в blendPixelAtBuffer
start
:Input dst pixel, src PMA pixel,\ntransform opacity;
if (pixel outside buffer?) then (yes)
stop
endif
if (replace_mode?) then (yes)
:dst = src;
stop
endif
:a = (src.a / 255) * opacity;
:inv_a = 1 - a;
:src_rgb = src.rgb * opacity;
:dst.r = clamp(src_r + inv_a * dst.r);
:dst.g = clamp(src_g + inv_a * dst.g);
:dst.b = clamp(src_b + inv_a * dst.b);
:dst.a = clamp(a*255 + inv_a * dst.a);
:Store dst pixel;
stop
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View File

@@ -0,0 +1,29 @@
@startuml
title Ломаная + заливка: 3-фазный алгоритм
start
:Input points[], closed, filled;
if (points < 2?) then (yes)
stop
endif
:Create temporary buffer + copy context;
if (closed and filled?) then (yes)
:Enable FillCanvas (startFill);
endif
:Draw all segments Pi->Pi+1;
if (closed?) then (yes)
:Draw segment Pn->P0;
endif
if (fill enabled?) then (yes)
:Phase 1:\nBorder pixels already collected in hash-set;
:Phase 2:\nSort border keys by (y,x),\nfind row segments,\nchoose interior seeds;
:Phase 3:\nStack flood fill (4-neighbors),\nskip border/visited,\npaint fill color;
endif
:Composite temporary buffer to target once;
stop
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

View File

@@ -0,0 +1,19 @@
@startuml
title Алгоритм композиции трансформаций (parent * local)
start
:Input parent transform:\nP(tx,ty,angle,sx,sy,opacity);
:Input local transform:\nL(tx,ty,angle,sx,sy,opacity);
:Scale local position by parent scale:\nlx' = L.tx * P.sx\nly' = L.ty * P.sy;
:Rotate by parent angle:\nrx = cos(P.a)*lx' - sin(P.a)*ly'\nry = sin(P.a)*lx' + cos(P.a)*ly';
:Translate by parent position:\nW.tx = P.tx + rx\nW.ty = P.ty + ry;
:Compose angle:\nW.angle = P.angle + L.angle;
:Compose scale:\nW.sx = P.sx * L.sx\nW.sy = P.sy * L.sy;
:Compose opacity:\nW.opacity = P.opacity * L.opacity;
:Return world transform W;
stop
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

View File

@@ -0,0 +1,49 @@
@startuml
title Zivro: компонентная архитектура
skinparam componentStyle rectangle
skinparam shadowing false
package "UI Layer" {
[main.zig\nEvent Loop]
[ui/*\nMenu/Tab/Panels]
[Canvas.zig\nViewport + Redraw]
}
package "Application State" {
[WindowContext.zig\nOpenDocument tabs]
[models/Document.zig\nObject tree]
[models/Object.zig\nProperties + Children]
}
package "Render Layer" {
[RenderEngine.zig\nInterface]
[CpuRenderEngine.zig\nCPU backend]
[cpu/draw.zig\nRecursive draw]
[cpu/pipeline.zig\nTransforms + blend]
[cpu/line.zig]
[cpu/ellipse.zig]
[cpu/broken.zig]
}
package "Persistence" {
[persistence/json_io.zig]
}
[main.zig\nEvent Loop] --> [WindowContext.zig\nOpenDocument tabs]
[ui/*\nMenu/Tab/Panels] --> [WindowContext.zig\nOpenDocument tabs]
[Canvas.zig\nViewport + Redraw] --> [RenderEngine.zig\nInterface]
[Canvas.zig\nViewport + Redraw] --> [models/Document.zig\nObject tree]
[WindowContext.zig\nOpenDocument tabs] --> [Canvas.zig\nViewport + Redraw]
[WindowContext.zig\nOpenDocument tabs] --> [CpuRenderEngine.zig\nCPU backend]
[RenderEngine.zig\nInterface] <|.. [CpuRenderEngine.zig\nCPU backend]
[CpuRenderEngine.zig\nCPU backend] --> [cpu/draw.zig\nRecursive draw]
[cpu/draw.zig\nRecursive draw] --> [cpu/pipeline.zig\nTransforms + blend]
[cpu/draw.zig\nRecursive draw] --> [cpu/line.zig]
[cpu/draw.zig\nRecursive draw] --> [cpu/ellipse.zig]
[cpu/draw.zig\nRecursive draw] --> [cpu/broken.zig]
[models/Document.zig\nObject tree] --> [models/Object.zig\nProperties + Children]
[persistence/json_io.zig] ..> [models/Document.zig\nObject tree] : save/load
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

View File

@@ -0,0 +1,33 @@
@startuml
title Zivro: последовательность рендера кадра (CPU)
actor User
participant "UI Frame\n(frame.zig)" as UI
participant "Canvas\n(Canvas.zig)" as Canvas
participant "RenderEngine" as RE
participant "CpuRenderEngine" as CPU
participant "drawDocument\n(cpu/draw.zig)" as Draw
participant "Shape modules\nline/ellipse/broken" as Shapes
User -> UI : input/events
UI -> Canvas : processPendingRedraw()
alt redraw pending and throttle passed
Canvas -> Canvas : computeVisibleImageRect()\ncompute quality scale
Canvas -> RE : render(document, canvas_size, visible_rect)
RE -> CPU : renderDocument(...)
CPU -> Draw : drawDocument(pixels,...)
loop for each root object
Draw -> Draw : Transform.compose(parent, local)
Draw -> Shapes : draw shape by object.kind
loop for each child
Draw -> Draw : recursive drawObject(child, world_transform)
end
end
Draw --> CPU : pixels rendered
CPU --> Canvas : texture
Canvas -> UI : texture for draw
else no redraw
Canvas -> UI : keep previous texture
end
@enduml