多角形から曲線(円弧補間(4点))を生成するスクリプトの修正版です。
設定した誤差以下になるか、頂点数が16000を超えるまで最大20回2分割を繰り返します。10回程度なら一瞬ですが、1回ごとに頂点数が倍々で増えて行くので20回繰り返すと最大十数秒かかります(VW2015デモ版にて)。
注意:このスクリプトにはバグがあります。
procedure PolyToCurve;
{ 選択した多角形から曲線を作る(放物線補間(4点、2等分法)) }
{$ DEBUG}
label
999, 1000;
const
MaxN = 16000;
PolygonObj = 5;
nL = 20;
type
PointImfo = structure
dir :integer;
p, pc :vector;
rad :real;
hasArc, isArc :boolean;
end;
var
pt :dynArray[] of PointImfo;
i, iL, n, np :integer;
d0, maxR :real;
p :vector;
hp, h, tmp :handle;
closed :boolean;
{$INCLUDE :vs_subroutins:If.vss}
{$INCLUDE :vs_subroutins:Basic.vss}
{$INCLUDE :vs_subroutins:Add_Inc_Dec.vss}
{$INCLUDE :vs_subroutins:KSSErrMsg.vss}
{$INCLUDE :vs_subroutins:Calc.vss}
{$INCLUDE :vs_subroutins:Small.vss}
{$INCLUDE :vs_subroutins:Large.vss}
{$INCLUDE :vs_subroutins:Swap.vss}
{$INCLUDE :vs_subroutins:SwapByOrder.vss}
{$INCLUDE :vs_subroutins:compound_type.vss}
{$INCLUDE :vs_subroutins:GetDefault.vss}
{$INCLUDE :vs_subroutins:Interpolation.vss}
{$INCLUDE :vs_subroutins:Math.vss}
{$INCLUDE :vs_subroutins:Math-Arc.vss}
{$INCLUDE :vs_subroutins:Drawing.vss}
{$INCLUDE :vs_subroutins:Drawing-New.vss}
{$INCLUDE :vs_subroutins:3d-Drawing.vss}
function GetPoints(h :handle): integer;
var
i, n :integer;
begin
closed:= IsPolyClosed(h);
n:= GetVertNum(h);
if MaxN < n then begin
Message(Concat('多角形の頂点が', MaxN, '個を超えています。(', iL, '回で打ち切り)'));
n:= 0;
end
else begin
Allocate pt[-1..n + 2];
for i:= 1 to n do begin{ 点座標の取得 }
GetPolyPt(h, i, pt[i].p.x, pt[i].p.y);
pt[i].p.z:= 0;
pt[i].hasArc:= closed | ((1 < i) & (i < n));
end;
if closed then begin
pt[n+1]:= pt[1]; pt[n+2]:= pt[2];
pt[-1]:= pt[n-1]; pt[0]:= pt[n];
end;
end;
GetPoints:= n;
end;{GetPoints}
function GetArcCenterAndDir(p9, p0, p1 :vector; var cnt :vector; var dir :integer): boolean;
var
ang, r :real;
v0, v1 :vector;
result :boolean;
begin
dir:= 0;
result:= SME2ArcCenterV(p9, p0, p1, cnt);
if result then begin
r:= Vec2Dist(p0 - cnt);
if r < maxR then begin
v0:= p0 - p9;
v1:= p1 - p0;
ang:= AzimuthByVecs(v0, v1);
dir:= Sign(ang);
end
else
result:= false;
end;
GetArcCenterAndDir:= result;
end;{GetArcCenterAndDir}
function GetNewPt(i :integer; var p :vector): boolean;
var
ang, rad, d, dst :real;
p0, p1, p2, p9, pc, pm :vector;
v, v0, v1, v2, v9, vc, vcc, vv :vector;
result :boolean;
begin
p0:= pt[i].p; p1:= pt[i+1].p;
pm:= (p0 + p1) / 2;
dst:= Vec2Dist(p1 - p0);
if pt[i].hasArc then begin
if pt[i].isArc then begin
v0:= pt[i].pc - p0;
v0:= RotVec(v0, -pt[i].dir * 90);
end
else
v0:= p1 - p0;
end;
if pt[i+1].hasArc then begin
if pt[i+1].isArc then
v1:= RotVec(pt[i+1].pc - p0, -pt[i+1].dir * 90)
else
v1:= p1 - p0;
end;
if pt[i].hasArc then
if pt[i+1].hasArc then
v:= VecVec2MidAzimuthVec(v0, v1)
else
v:= v0
else if pt[i+1].hasArc then
v:= v1;
if SVE2ArcCenterV(p0, v, p1, pc) then begin
rad:= Vec2Dist(p0 - pc);
if rad > maxR then
p:= pm
else begin
ang:= Vec2Ang(p1 - p0);
vc:= UnitVec(pc - pm);
vcc:= RotVec(vc, -ang);
vv:= RotVec(v, -ang);
p:= pc + Sign(vcc.y) * Sign(vv.y) * vc * rad;
end;
end
else
p:= pm;
d:= Vec2Dist(p - pm);
result:= (d0 < d);
if not result then begin
v2:= RotVec(p1 - pt[i+1].pc, 90);
if IntersectLLV(pc, pm, p1, p1 + v2, p2) then begin
if Vec2Dist(p2 - pm) > (2 * d0) then
result:= true
else begin
v9:= RotVec(p0 - pt[i].pc, 90);
if IntersectLLV(pc, pm, p0, p0 + v9, p9) then
result:= (Vec2Dist(p9 - pm) > (2 * d0));
end;
end;
end;
GetNewPt:= result;
end;{GetNewPt}
begin {main}
hp:= FSActLayer;
h:= hp;
if (h = Nil) | (GetTypeN(h) <> PolygonObj) then GoTo 1000;
MaxR:= LargeR(HWidth(h), HHeight(h)) / PrecFactor;
d0:= 4/9 * GetDefaultWS('曲線', '誤差', 1.0, true);
iL:= 0;
while (iL < nL) do begin
n:= GetPoints(h);
if n < 3 then GoTo 999;
iL:= iL + 1;
for i:= 1 to n do begin
if pt[i].hasArc then begin{ 円弧の中心と回転方向を取得 }
pt[i].isArc:= GetArcCenterAndDir(pt[i-1].p, pt[i].p, pt[i+1].p, pt[i].pc, pt[i].dir);
if pt[i].isArc then begin
pt[i].rad:= Vec2Dist(pt[i-1].p - pt[i].pc);
end;
end;
end;
if closed then begin
pt[n+1]:= pt[1]; pt[n+2]:= pt[2];
pt[-1]:= pt[n-1]; pt[0]:= pt[n];
end;
if closed then
ClosePoly
else
OpenPoly;
Message(Concat(iL, '回目'));
np:= 0;
BeginPoly;
for i:= 1 to n do begin
AddPointV(pt[i].p);
if closed | (i < n) then begin
if GetNewPt(i, p) then begin
AddPointV(p);
np:= np + 1;
end;
end;
end;
EndPoly;
if h <> hp then
DelObject(h);
h:= LNewObj;
ReDrawAll;
if np = 0 then begin
iL:= nL + iL;
end;
end;
if iL <= nL then
if np = 0 then
Message(Concat('分割回数 = ', iL-1))
else
Message(Concat('分割回数 = ', iL, ' (打ち切り)'))
else
Message(Concat('分割回数 = ', iL-nL-1));
999:
SetDSelect(hp);
ReDrawAll;
1000:
end; {main}
Run(PolyToCurve);