Report
BIN
Report/uml/canvas-viewport-algorithm.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
35
Report/uml/canvas-viewport-algorithm.puml
Normal 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
|
||||
BIN
Report/uml/coordinate-pipeline.png
Normal file
|
After Width: | Height: | Size: 125 KiB |
28
Report/uml/coordinate-pipeline.puml
Normal 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
|
||||
BIN
Report/uml/ellipse-arc-rasterization.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
43
Report/uml/ellipse-arc-rasterization.puml
Normal 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
|
||||
BIN
Report/uml/liang-barsky-clip.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
34
Report/uml/liang-barsky-clip.puml
Normal 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
|
||||
BIN
Report/uml/line-rasterization-flow.png
Normal file
|
After Width: | Height: | Size: 80 KiB |
30
Report/uml/line-rasterization-flow.puml
Normal 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
|
||||
BIN
Report/uml/pma-alpha-blending.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
27
Report/uml/pma-alpha-blending.puml
Normal 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
|
||||
BIN
Report/uml/polyline-fill-algorithm.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
29
Report/uml/polyline-fill-algorithm.puml
Normal 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
|
||||
BIN
Report/uml/transform-compose-algorithm.png
Normal file
|
After Width: | Height: | Size: 80 KiB |
19
Report/uml/transform-compose-algorithm.puml
Normal 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
|
||||
BIN
Report/uml/zivro-architecture-components.png
Normal file
|
After Width: | Height: | Size: 91 KiB |
49
Report/uml/zivro-architecture-components.puml
Normal 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
|
||||
BIN
Report/uml/zivro-render-sequence.png
Normal file
|
After Width: | Height: | Size: 106 KiB |
33
Report/uml/zivro-render-sequence.puml
Normal 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
|
||||