Skip to content

Commit fd48121

Browse files
committed
Reduced number of shallow copies.
+ Anchors for cubic Bezier curves are now colored.
1 parent ff08fa4 commit fd48121

3 files changed

Lines changed: 96 additions & 76 deletions

File tree

@Path_S.obj2

Lines changed: 72 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
-- under development for v1.20 (for beta42) r7
1+
-- under development for v1.20 (for beta42) r8
22
--[[
33
MIT License
44
Copyright (c) 2025-2026 sigma-axis
@@ -422,7 +422,7 @@ end
422422

423423
-- measure and move the path.
424424
path_s.transform(pts, n_pts, 1, math.atan2(Y, X));
425-
local L, R, T, B, len = path_s.measure(pts, n_pts);
425+
local L, R, T, B = path_s.measure(pts, n_pts);
426426
local th = math.ceil(line * (end_shape == 1 and 0.5 ^ 0.5 or 0.5) + antialias);
427427
L, T = math.floor(L - th), math.floor(T - th);
428428
R, B = math.max(math.ceil(R + th), L + 1), math.max(math.ceil(B + th), T + 1);
@@ -434,7 +434,7 @@ obj.clearbuffer("object", R - L, B - T, color);
434434
-- draw the line.
435435
path_s.path_mask_line(
436436
0, 1, line, antialias,
437-
0, pts, n_pts - 1, false, 1,
437+
nil, pts, n_pts - 1, false, 1,
438438
0, 1, end_shape, dash_pat, dash_pos, false,
439439
1, 0, obj.cx, obj.cy);
440440

@@ -614,7 +614,7 @@ obj.clearbuffer("object", R - L, B - T, color);
614614
-- draw the path.
615615
path_s.path_mask_line(
616616
0, 1, line, antialias,
617-
0, pts, n_pts - 1, false, 1,
617+
nil, pts, n_pts - 1, false, 1,
618618
0, 1, end_shape, dash_pat, dash_pos, false,
619619
1, 0, obj.cx, obj.cy);
620620

@@ -782,86 +782,93 @@ if rand_amplify > 0 then
782782
rand_period, rand_amplify, 0, rand_seed);
783783
end
784784
end
785-
local L, R, T, B, len = path_s.measure(points, num_points);
785+
local L, R, T, B = path_s.measure(points, num_points);
786786
local th = line * (end_shape == 1 and 0.5 ^ 0.5 or 0.5) + antialias;
787787
if head_type ~= 0 then
788788
-- take the arrow heads into account.
789789
th = math.max(th, head_size / 2 * ((math.abs(head_center) + 1) ^ 2 + head_width ^ 2) ^ 0.5);
790790
end
791791
L, T = math.floor(L - th), math.floor(T - th);
792792
R, B = math.max(math.ceil(R + th), L + 1), math.max(math.ceil(B + th), T + 1);
793+
local W, H, cx, cy = R - L, B - T, -(L + R) / 2, -(T + B) / 2;
794+
795+
-- calculate the head position.
796+
local head_vertices = nil;
797+
if head_type ~= 0 and head_size > 0 and head_width > 0 then
798+
local function polygon(i, j, rot)
799+
local X, Y =
800+
(1 - j) * points[2 * i - 1] + j * points[2 * i + 1],
801+
(1 - j) * points[2 * i - 0] + j * points[2 * i + 2];
802+
local A = math.atan2(
803+
points[2 * i + 2] - points[2 * i - 0],
804+
points[2 * i + 1] - points[2 * i - 1]);
805+
806+
-- as to the angle, interpolate with the neighbor secant.
807+
if j < 0.5 then i = i - 1;
808+
else i, j = i + 1, 1 - j end
809+
local dA = 0;
810+
if 0 < i and i < num_points then
811+
dA = math.atan2(
812+
points[2 * i + 2] - points[2 * i - 0],
813+
points[2 * i + 1] - points[2 * i - 1]);
814+
dA = (dA - A) / (2 * math.pi);
815+
dA = ((dA + 0.5) % 1) - 0.5;
816+
dA = 2 * math.pi * (0.5 - j) * dA;
817+
end
818+
A = A + dA + rot;
819+
820+
local c, s = math.cos(A), math.sin(A);
821+
X, Y =
822+
X - head_center * head_size / 2 * s + cx,
823+
Y + head_center * head_size / 2 * c + cy;
824+
local w2x, w2y, h2x, h2y = head_size * head_width / 2, 0, 0, head_size / 2;
825+
w2x, w2y = w2x * c, w2x * s;
826+
h2x, h2y = h2y *-s, h2y * c;
827+
return
828+
{ X - w2x - h2x, Y - w2y - h2y, 0; 0, 0 },
829+
{ X + w2x - h2x, Y + w2y - h2y, 0; 1, 0 },
830+
{ X + w2x + h2x, Y + w2y + h2y, 0; 1, 1 },
831+
{ X - w2x + h2x, Y - w2y + h2y, 0; 0, 1 };
832+
end
833+
834+
-- first tip.
835+
local pos = math.min(math.max(1 + head_pos, 0), 1);
836+
pos = (1 - pos) * start_pos + pos * end_pos;
837+
local i, j, l = path_s.find_index(-pos, points, num_points);
838+
local vts = { polygon(i, j, head_rot + math.pi / 2) };
839+
840+
if head_type > 1 then
841+
-- second tip.
842+
local pos2 = math.min(math.max(head_pos, 0), 1);
843+
pos2 = (1 - pos2) * start_pos + pos2 * end_pos;
844+
if pos ~= pos2 then
845+
i, j = path_s.find_index(-pos2, l);
846+
vts[5], vts[6], vts[7], vts[8] = polygon(i, j, head_rot
847+
+ math.pi / 2 * (head_type == 2 and 1 or -1));
848+
end
849+
end
850+
851+
head_vertices = vts;
852+
end
793853

794854
-- draw the path.
795-
local cx, cy = -(L + R) / 2, -(T + B) / 2;
796-
obj.clearbuffer("object", R - L, B - T, color);
855+
obj.clearbuffer(head_vertices and "tempbuffer" or "object", W, H, color);
797856
path_s.path_mask_line(
798857
0, 1, line, antialias,
799-
0, points, num_points - 1, false, 1,
858+
nil, points, num_points - 1, false, 1,
800859
start_pos, end_pos, end_shape, dash_pat, dash_pos, false,
801-
1, 0, cx, cy);
860+
1, 0, cx, cy,
861+
head_vertices and { name = "tempbuffer", w = W, h = H } or nil,
862+
head_vertices and "object" or nil);
802863

803864
-- draw the arrow heads.
804-
if head_type ~= 0 and head_size > 0 and head_width > 0 then
865+
if head_vertices then
805866
obj.setoption("drawtarget", "tempbuffer");
806-
obj.copybuffer("tempbuffer", "object");
807867
obj.load("figure", head_fig, color, math.ceil(head_size * math.max(head_width, 1)));
808868
if obj.w > 0 and obj.h > 0 then
809-
local function polygon(i, j, rot)
810-
local X, Y =
811-
(1 - j) * points[2 * i - 1] + j * points[2 * i + 1],
812-
(1 - j) * points[2 * i - 0] + j * points[2 * i + 2];
813-
local A = math.atan2(
814-
points[2 * i + 2] - points[2 * i - 0],
815-
points[2 * i + 1] - points[2 * i - 1]);
816-
817-
-- as to the angle, interpolate with the neighbor secant.
818-
if j < 0.5 then i = i - 1;
819-
else i, j = i + 1, 1 - j end
820-
local dA = 0;
821-
if 0 < i and i < num_points then
822-
dA = math.atan2(
823-
points[2 * i + 2] - points[2 * i - 0],
824-
points[2 * i + 1] - points[2 * i - 1]);
825-
dA = (dA - A) / (2 * math.pi);
826-
dA = ((dA + 0.5) % 1) - 0.5;
827-
dA = 2 * math.pi * (0.5 - j) * dA;
828-
end
829-
A = A + dA + rot;
830-
831-
local c, s = math.cos(A), math.sin(A);
832-
X, Y =
833-
X - head_center * head_size / 2 * s + cx,
834-
Y + head_center * head_size / 2 * c + cy;
835-
local w2x, w2y, h2x, h2y = head_size * head_width / 2, 0, 0, head_size / 2;
836-
w2x, w2y = w2x * c, w2x * s;
837-
h2x, h2y = h2y *-s, h2y * c;
838-
return
839-
{ X - w2x - h2x, Y - w2y - h2y, 0; 0, 0 },
840-
{ X + w2x - h2x, Y + w2y - h2y, 0; 1, 0 },
841-
{ X + w2x + h2x, Y + w2y + h2y, 0; 1, 1 },
842-
{ X - w2x + h2x, Y - w2y + h2y, 0; 0, 1 };
843-
end
844-
845-
-- first tip.
846-
local pos = math.min(math.max(1 + head_pos, 0), 1);
847-
pos = (1 - pos) * start_pos + pos * end_pos;
848-
local i, j, l = path_s.find_index(-pos, points, num_points);
849-
local vts = { polygon(i, j, head_rot + math.pi / 2) };
850-
851-
if head_type > 1 then
852-
-- second tip.
853-
local pos2 = math.min(math.max(head_pos, 0), 1);
854-
pos2 = (1 - pos2) * start_pos + pos2 * end_pos;
855-
if pos ~= pos2 then
856-
i, j = path_s.find_index(-pos2, l);
857-
vts[5], vts[6], vts[7], vts[8] = polygon(i, j, head_rot
858-
+ math.pi / 2 * (head_type == 2 and 1 or -1));
859-
end
860-
end
861-
862869
-- "alpha_max" does not seem to work well with obj.drawpoly().
863870
-- obj.setoption("blend", "alpha_max");
864-
obj.drawpoly(vts);
871+
obj.drawpoly(head_vertices);
865872
-- obj.setoption("blend");
866873
end
867874
obj.copybuffer("object", "tempbuffer");

Path_S.lua

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
-- under development for v1.20 (for beta42) r7
1+
-- under development for v1.20 (for beta42) r8
22
--[[
33
MIT License
44
Copyright (c) 2025-2026 sigma-axis
@@ -108,16 +108,22 @@ local anchor, poll do
108108
obj.setanchor(var_name, n_anchors, unpack1(alt_pts));
109109

110110
-- draw handles.
111-
local pts2 = alt_pts or pts;
111+
local pts2, pts3 = alt_pts or pts, {};
112112
for i = 1, n_segs do
113113
local I, J = 6 * i, 6 * (loop and (i % n_segs) or i);
114114
obj.setanchor({
115-
pt(pts2, I - 3), pt(pts2, I - 2),
116115
pt(pts2, I - 5), pt(pts2, I - 4),
117-
pt(pts2, J + 1), pt(pts2, J + 2),
116+
pt(pts2, I - 3), pt(pts2, I - 2),
118117
pt(pts2, I - 1), pt(pts2, I - 0),
119-
}, 4, "line");
118+
pt(pts2, J + 1), pt(pts2, J + 2),
119+
}, 4, "line", "inout");
120+
pts3[2 * i - 1], pts3[2 * i] = pt(pts2, I - 5), pt(pts2, I - 4);
120121
end
122+
if not loop then
123+
pts3[2 * n_segs + 1], pts3[2 * n_segs + 2] =
124+
pt(pts2, 6 * n_segs + 1), pt(pts2, 6 * n_segs + 2);
125+
end
126+
obj.setanchor(pts3, n_segs + (loop and 0 or 1), loop and "loop" or "line");
121127
else obj.setanchor(var_name, n_anchors, loop and "loop" or "line", unpack1(alt_pts)) end
122128
return n_anchors, alt_pts or pts;
123129
end
@@ -534,7 +540,7 @@ end
534540
---@param mode_fill mode_fill 塗りつぶし範囲の指定.
535541
---@param inflation number 「追加幅」をピクセル単位で指定.
536542
---@param antialias number 「ぼかし幅」をピクセル単位で指定.
537-
---@param path_type path_type 「線タイプ」(パスの種類) を指定.
543+
---@param path_type path_type|nil 「線タイプ」(パスの種類) を指定`nil` を指定した場合,折れ線への変換や点列のコピーを省略する.このとき `pts` は既に折れ線の前提で,テーブルの内容も書き換わる
538544
---@param pts { [integer]: number? } 点列の配列, `{ x1, y1, x2, y2, x3, y3, ... }` の形式.
539545
---@param n_segs integer パスの分割区間の個数.
540546
---@param prec number 「曲線精度」(折れ線の許容最長距離) を指定.
@@ -560,8 +566,11 @@ local function path_mask_area(
560566
if alpha_outer == alpha_inner then mask_uniform(alpha_outer, tgt_name); return end
561567

562568
-- make the curve into the sequence of secants.
563-
local points, num_points = poll(path_type, pts, n_segs, true,
564-
prec / math.min(math.max(scale, 1 / 64), 1));
569+
local points, num_points = pts, n_segs + 1;
570+
if path_type then
571+
points, num_points = poll(path_type, pts, n_segs, true,
572+
prec / math.min(math.max(scale, 1 / 64), 1));
573+
end
565574

566575
-- apply translation / scaling / rotation.
567576
transform(points, num_points, scale, rotate, dx, dy);
@@ -757,7 +766,7 @@ end
757766
---@param alpha_inner number パス内側のマスクのアルファ値を 0.0 から 1.0 で指定.
758767
---@param line_width number 「ライン幅」をピクセル単位で指定.
759768
---@param antialias number 「ぼかし幅」をピクセル単位で指定.
760-
---@param path_type path_type 「線タイプ」(パスの種類) を指定.
769+
---@param path_type path_type|nil 「線タイプ」(パスの種類) を指定`nil` を指定した場合,折れ線への変換や点列のコピーを省略する.このとき `pts` は既に折れ線の前提で,テーブルの内容も書き換わる
761770
---@param pts { [integer]: number? } 点列の配列, `{ x1, y1, x2, y2, x3, y3, ... }` の形式.
762771
---@param n_segs integer パスの分割区間の個数.
763772
---@param loop boolean 閉じたパスかどうか.
@@ -790,8 +799,11 @@ local function path_mask_line(
790799
if alpha_outer == alpha_inner then mask_uniform(alpha_outer, tgt_name); return end
791800

792801
-- make the curve into the sequence of secants.
793-
local points, num_points = poll(path_type, pts, n_segs, loop,
794-
prec / math.min(math.max(scale, 1.0 / 64), 1));
802+
local points, num_points = pts, n_segs + 1;
803+
if path_type then
804+
points, num_points = poll(path_type, pts, n_segs, loop,
805+
prec / math.min(math.max(scale, 1.0 / 64), 1));
806+
end
795807

796808
-- apply translation / scaling / rotation.
797809
transform(points, num_points, scale, rotate, dx, dy);

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -988,6 +988,7 @@ radii = { uniform = 10; { 16, 8 }, nil, 20, nil }
988988
- **v1.20 (for beta42)** (2026-??-??)
989989

990990
- 「パス部分フィルタσ」で後続フィルタの代わりにスクリプトの直接記入をできるように.
991+
- 3次ベジェ曲線のアンカーのハンドルを色分けして表示するように.
991992
- 「スクウェアσ」で「四角」の端点が正しく描画されなかったことがあったのを修正.
992993
- マスク系のフィルタ効果でパスが画像範囲外のとき,「強さ」が常に 100% 扱いだったのを修正.
993994
- 前回更新からの AviUtl2 本体のスクリプト機能追加を適用 (`--trackgroup` 指定やトラックバーの操作倍率など). それに伴って一部トラックバーの最大・最小の範囲を拡大.

0 commit comments

Comments
 (0)