Но попробую повторить!
В начале о том, что по моему можно получить с помощью "парсинга формата SHA".
1 Это открывает возможность для создания препроцессоров и расширения формата
2 Появляется возможность обработки SHA файлов и создания их более читаемой и редактируемой вручную версии.
3 В перспективе можно написать "виртуальную скрипт-машину" исполняющую схемы в рун-тайм режиме.
4 И наконец то из за чего я взялся за это "неблагодарное дело": парсинг формата SHA открывает возможность для написания внешнего просмотрщика (а в перспективе редактора) схем.
Мой тестовый модуль parsing_sha.pas
Написан в Лазарусе.
(но я специально провозился почти неделю чтобы максимально отвязать его от среды )
В принципе основная там процедура SHA2TXT транслирующая код сехмы в слегка модифицированную версию данных для демки HiAsm3d.(я в своей программе первым делом повторил просмотр тамошней "псевдо схемы", а позднее решил, что от добра добра не ищут и не стал доводить новый вариант 3д-прорисовки до товарного вида и сделал внутренний конвертр)
Особнности.
1 Контейнеры еще не поддерживает
(Рекурсивный парсинг контейнеров есть но я его не использую )
2 Хабы пока тоже не показывает ( что довольно обидно)
3 Не видит "точки из свойств" (которые появляются при клике на галочку в инспекторе )
4 Процедура CreateOrLoadElementBase умеет перелопачивать Ini-файлы с описаниями элементов во внутренний формат.
Процедура ParserElementBase их парсит
Процедура SHA_Parser парсит непосредственно схему.
И наконц процедура SHA2TXT транслирующая код сехмы (а по сути рисующая схему )
unit parsing_sha;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,FileUtil,LazFileUtils,IniFiles,Math, Graphics;
type
TSPoint=Record
Name:String;
VName:String;
Rem:String;
PointType:Integer; {1-Left(do) 2-Top(on) 3-Right(Var) 4-Bottom(Data) }
PointDataType:String;
X,Y:Integer;
Option:String;
end;
TSPsram=Record
Name:String;
Data:String;
end;
TSLink=Record
PointName:String;
Id :String;
PointTO:String;
LinkCP:String;
end;
PSPoint=^TSPoint;
PSPsram=^TSPsram;
PSLink =^TSLink;
TSElement=class;
TSElementDrawProc = procedure (EL:TSElement);
TSLinkDrawProc = procedure (EL:TSElement);
TSElement=class(TObject)
PointList:Tlist;
ParamList:TList;
LinkList:Tlist;
// SHACode:STring;
Name:STring;
ID:STring;
eX,eY,CW,CH:Integer;
SubElementList:TStringList;
Cdo,Con,Cdat,Cvar:Integer;
Constructor Create;//override;
//Constructor CreateFromSHA(FCode:STring);
destructor Destroy; override;
Procedure DrawElement;
const
StepX :Integer=7; //Шаг X
StepY :Integer=7; //Шаг X
WW:Integer=-1;
HH:Integer=-1;
Procedure CalkMetrix;
end;
var
SElementDraw:TSElementDrawProc;
const
TextSHA:TStringList=nil;
ElementList:TStringList=nil;
BaseElementDrawList:TStringList=nil;
MiniSHA:TStringList=nil;
Procedure CreateOrLoadElementBase(IniDir,baseName :String;Upd:Boolean;var TS:TStringList);
Procedure ParserElementBase(var TS:TStringList);
Procedure SHA_Parser(TextSHA:TStringList);
function IndexOfPointItem(N:String;L:Tlist;T:Boolean=False ):integer;
Procedure ClearPointItem(Var It:PSPoint );
Procedure FreePointItem(Var It:PSPoint );
// Конвертор SHA 2 TXT
procedure SHA2TXT;
implementation
const
StepX :Integer=7; //Шаг X
StepY :Integer=7; //Шаг X
MinW :Integer=3; //Минмальная шрина чипа (в шагах)
MinH :Integer=4; //Минмальная высота чипа (в шагах)
otX:Integer=1; // отсуп X (в шагах)
otY:Integer=1; // отсуп Y (в шагах)
ZDX:Integer=0; // сдвиг X
ZDY:Integer=0; // сдвиг Y
Procedure TSElement.CalkMetrix;
Function CalkIt(var LP:TList;T:Integer):Integer;
var I,J:Integer;
C2: PSPoint;
begin
If Lp ‹› nil then begin
J:=0; for I:=0 to LP.Count-1 do
begin
C2:= PSPoint ( LP[i] );
if (C2^.PointType=T) and (C2^.Option‹›'/Hide') then
J:=J+1;
end;
Result:=J;
end
else Result:=0;
end;
Procedure CalkItXY(var LP:TList;T:Integer);
var x,y,I,J:Integer;
C2: PSPoint;
begin
If Lp ‹› nil then begin
X:=Ex; Y:=Ey;
J:=0; for I:=0 to LP.Count-1 do
begin
C2:= PSPoint ( LP[i] );
if (C2^.PointType=T) and (C2^.Option‹›'/Hide') then begin
J:=J+1;
Case T of
1:begin C2^.x:=x; C2^.Y:=y+StepY*J; end;
2:begin C2^.x:=x+StepX*WW; C2^.Y:=y+StepY*J; end;
3:begin C2^.x:=x+StepX*J; C2^.Y:=Y+StepY*HH; end;
4:begin C2^.x:=x+StepX*J; C2^.Y:=y; end;
end;
end
end
end
end;
begin
Cdo:= CalkIt(PointList,1);
Con:= CalkIt(PointList,2);
CVar:= CalkIt(PointList,3);
Cdat:= CalkIt(PointList,4);
WW:=Max(CDo,Max(COn,minW));
HH:=Max(CDat,Max(CVar,minH));
CalkItXY(PointList,1);
CalkItXY(PointList,2);
CalkItXY(PointList,3);
CalkItXY(PointList,4);
end;
Procedure TSElement.DrawElement;
begin
SElementDraw(Self);
end;
destructor TSElement.Destroy;
Var
EN:TListEnumerator;
begin
EN:=PointList.GetEnumerator;
If PointList.Count ›0 then
while En.MoveNext do begin
PSPoint(EN.Current)^.Name:='';
PSPoint(EN.Current)^.VName:='';
PSPoint(EN.Current)^.Rem:='';
PSPoint(EN.Current)^.Option:='';
PSPoint(EN.Current)^.PointDataType:='';
Dispose( PSPoint(EN.Current) );
end; EN.Free;
PointList.free;
EN:=ParamList.GetEnumerator;
If ParamList.Count ›0 then
while En.MoveNext do begin
PSPsram(EN.Current)^.Name:='';
PSPsram(EN.Current)^.Data:='';
Dispose( PSPsram(EN.Current) );
end; EN.Free;
ParamList.free;
EN:=LinkList.GetEnumerator;
If LinkList.Count ›0 then
while En.MoveNext do begin
PSLink(EN.Current)^.PointName :='';
PSLink(EN.Current)^.Id :='';
PSLink(EN.Current)^.PointTO:='';
PSLink(EN.Current)^.LinkCP :='';
Dispose( PSLink(EN.Current) );
end; EN.Free;
LinkList.free;
{
if PointDo‹›Nil then PointDo.free;
if PointOn‹›Nil then PointOn.free;
if PointData‹›Nil then PointData.free;
if PointVar‹›Nil then PointVar.free;
if ExtPointDo‹›Nil then ExtPointDo.free;
}
inherited Destroy;
end;
Constructor TSElement.Create;
begin
inherited Create;
{PointDo:=Nil;PointOn:=Nil;
PointVar:=Nil;PointData:=Nil;}
PointList:=Tlist.Create;
ParamList:=TList.Create;
LinkList :=Tlist.Create;
SubElementList:=Nil;
end;
function IndexOfPointItem(N:String;L:Tlist;T:Boolean=False ):integer;
Var
I:Integer;
begin
Result:=-1; If L = Nil then exit;
If l.Count›0 then
For I:=0 to l.Count-1 do
if PSPoint(l[i])^.Name‹› '' then begin
If t then Writeln(PSPoint(l[i])^.Name);
if PSPoint(l[i])^.Name=N then
begin
Result:=i; exit;
end;
end;
end;
Procedure ClearPointItem(Var It:PSPoint );
begin
If It = Nil then exit;
It^.Name:=''; It^.VName:=''; It^.Rem:='';
It^.PointDataType:=''; It^.Option:=''; It^.PointType:=0;
end;
Procedure FreePointItem(Var It:PSPoint );
begin
If It = Nil then exit;
ClearPointItem(It);
Dispose( It ); It:=nil;
end;
function IsPointInRegion(Rect:Trect;AX, AY: Integer): Boolean;
begin
Result := (AX ›= Rect.Left) and (AX ‹= Rect.Right) and
(AY ›= Rect.Top) and (AY ‹= Rect.Bottom);
end;
Function I2S(F:Integer; const d1:byte=1):String;
var
E:Integer;
Begin
Str(F:D1,Result);
end;
Function S2I(S:String; const d:Integer=0 ):Integer;
var E:Integer;
Begin
Val(S,Result,E);
If E ‹› 0 then Result:=d;
end;
Function S2F(S:String;const d:Extended=0):Extended ;
var E:Integer;
Begin
Val(S,Result,E);
If E ‹› 0 then Result:=d;
end;
Function F2S(F:Extended; const d1:byte=1;d2:byte=4):String;
var E:Integer;
Begin
Str(F:D1:D2,Result);
end;
// Мини парсер строк
Function Next1(Var SS:String;Ch:Char):String;
var i:Longint;
begin
Result:='';
I:=Pos(Ch,SS);
If i›0 then begin
Result:= Copy (ss,1,i-1);
delete(SS,1,i);
end
end;
Function InR(AA,B,C:Longint):Boolean;
begin
InR:=((AA›=B) And (AA‹=C));
End;
Procedure CreateOrLoadElementBase(IniDir,baseName :String;Upd:Boolean;var TS:TStringList);
Var
FS:TFileStream;
ET:TSElement;
CPo: PSPoint;
// MM: THeapStatus;
Var
S:String;
K,I,J:Integer;
begin
// Загрузказка *.ini в мини базу 'Delphi.dat'
If Not FileExists (baseName) or UPD then
begin
Try
TS:=FindAllFiles( IniDir , '*.ini',false);
For I:=0 to ts.Count-1 do begin
ts.Objects[i]:=TStringList.Create;
TStringList(ts.Objects[i]).LoadFromFile(ts[I]);
Ts[I]:=
ExtractFileName( ExtractFileNameWithoutExt(TS[i]));
end;
FS:=TFileStream.Create(baseName,fmCreate);
fs.Seek(0,soFromBeginning);
For I:=0 to ts.Count-1 do Begin
fs.WriteAnsiString(TS[i]);
For J:=0 to TStringList(ts.Objects[i]).Count-1 do
begin
S:= TStringList(ts.Objects[i])[J];
fs.WriteAnsiString(S);
//Writeln(cp1251toUtf8());
end;
fs.WriteAnsiString('Element_END');
//Writeln;
end;
finally
fs.Free;
end
end;
Try
FS:=TFileStream.Create(baseName,fmOpenRead);
//TS:=TStringList.Create;
fs.Seek(0,soFromBeginning);
While FS.Position‹fs.Size do Begin
TS.AddObject( fs.ReadAnsiString,TStringList.Create);
S:= fs.ReadAnsiString;
While S‹›'Element_END' do begin
TStringList(ts.Objects[ts.Count-1]).Add(S);
S:= fs.ReadAnsiString;
end;
end;
finally
Fs.Free;
end;
end;
Procedure ParserElementBase(var TS:TStringList);
Var
FS:TFileStream;
ET:TSElement;
CPo: PSPoint;
// MM: THeapStatus;
procedure ParsOne(NN:String);
Var
K,I,J:Integer;
Mi: TMemIniFile;
S1,S,TN,VN:String;
EEP,SS,MS:TStringList;
C: PSPoint;
// C_P:TC_Point;
begin
// Доступ данным по названию элемента
Ms:=TStringList(TS.Objects[ts.IndexOf(NN)]);//.Text;
// Создаю класс TMemIniFile для доступа к разделам
MI:=TMemIniFile.Create('Test'); Mi.SetStrings(MS);
// Обработка "множественного наследования"
S1:=mi.ReadString('Type','Inherit','');
IF S1‹›'' then begin //write('[',s1,'] ');
Repeat
S:=Next1(s1,','); //!!! Рекурсия
If S‹›'' then ParsOne(S) else ParsOne(S1);
Until S='';
end;
{}
// Простой парсинг описания контактных точек
ss:=TStringList.Create;
mi.ReadSectionRaw('Methods',SS);
For I:=0 to ss.Count-1 do begin
S:=SS[I];
//Групы игнорирую...
If Pos('##',S)=1 Then continue;
If S='' Then continue;
TN:=Next1(s,'=');
//Вырезаю (если есть) дополнительное имя
If TN‹›'' Then begin
New(CPo);VN:='';
CPo^.Option:='';
If Pos('[',Tn)›0 then begin
VN:=TN;
TN:=Next1(VN,'[');
VN:=Next1(VN,']');
end;
if vn‹›'' then
CPo^.VName:=VN
else CPo^.VName:='' ;
//--------------------
//Учитываю опции в имени
J:= Pos('*',Tn);
If (J›0) then begin
CPo^.Option:=CPo^.Option+'/Hide';
Next1(TN,'*');
end;
CPo^.Name:=TN;
{ //Задел на будущее для свойств 'Property' ;
J:= Pos('@',Tn);
If (J›0) then begin
Delete(TN,J,1);
CPo^.Option:=CPo^.Option+'/CPoint';
end;
J:= Pos('+',Tn);
If (J›0) then begin
Delete(TN,J,1);
CPo^.Option:=CPo^.Option+'/CUOpen';
end;
CPo^.Name:=NewStr(TN);
end}
//Пасинг параметров с учетом "хитрого" кодирования
//особых символов внутири строк
S:=StringReplace(S,'||','◙◙',[rfReplaceAll]);
S1:= Next1(S,'|');
S1:=StringReplace(S1,'◙◙','||',[rfReplaceAll]);
CPo^.Rem:= S1 ;
CPo^.PointType:=StrToInt( Next1(S,'|'));
CPo^.PointDataType:=S;
//Перезагрузка точек при "наследовании".
J:=IndexOfPointItem(TN,et.PointList);
If J‹›-1 then begin
C:=PSpoint(et.PointList[j]);
FreePointItem( C );
ET.PointList[J]:=CPO;
end else ET.PointList.Add(CPO);
//-------------------------------
end else continue;
end; //For
(* *)
ss.Free;
mi.Free;
end;
Var
S:String;
K,I,J:Integer;
begin
If BaseElementDrawList=nil then
BaseElementDrawList:=TStringList.Create
else BaseElementDrawList.clear;
For K:=0 to Ts.Count-1 do begin
//Mm:=GetHeapStatus;
//WRiteln ('Free Memory ',mm.TotalFree);
ET:=TSElement.Create;
ET.Name:=TS[K];
// Memo2.Lines.Add('----› '+ET.Name);
//Вызваю рекусивный парсер для перовго уровня.
ParsOne(TS[K]);
//Заношу резуьтат в список
BaseElementDrawList.AddObject(ts[K],Et);
end; //for K
end ;
Procedure SHA_Parser(TextSHA:TStringList);
Var
LP,NL, N,Z,Sx,Sy,Nam,ID,S,s1,Hed1,hed2:String;
MC,TE,J,I,K,Y,lev,EI,IP:Integer;
//Рекурсивный парсер контейнеров схемы
procedure ParserOneLev (EL:TStringList);
Var
BE,CE: TSElement;
CPo,BP: PSPoint;
CPr: PSPsram;
CLk: PSLink;
begin
repeat
if I › TextSHA.Count-1 then exit;
//else //END_SDK
While pos('Add(',TextSHA[I])=0 do
if I ‹ TextSHA.Count-1 then i:=i+1 else exit;
//i:=i+1;
S:=TextSHA[I];
Next1(S,'('); Nam:=Next1(S,',');
id:=Next1(S,',');
Sx:=Next1(S,',');
SY:=Next1(S,')');
CE:=TSElement.Create;
Ce.Name:=Nam;
Ce.ID:=id;
Ce.eX:=StrToInt(SX);
Ce.eY:=StrToInt(SY);
//------------------------------
// Поиск начала "тела" элемента
While pos('{',TextSHA[I])=0 do
if I‹ TextSHA.Count then i:=i+1 else begin ce.Free; exit; end;
i:=i+1;
// Если нет завершения ...
While pos('}',TextSHA[I])=0 do begin
S:=TextSHA[I];
// Поиск связей.
if pos('link(',TextSHA[I])‹›0 then begin
Next1(S,'('); N:=Next1(S,',');
ID:=Next1(S,':');NL:=Next1(S,',');
Next1(S,'['); LP:=Next1(S,']');
New(CLk);
CLK^.PointName:=N;
CLK^.Id:=Id;
CLK^.PointTO:=NL;
CLK^.LinkCP:=LP;
ce.LinkList.Add(Clk);
end else
// Параметры
if pos('=',TextSHA[I])‹›0 then begin
While S[1]=' ' do delete(S,1,1);
N:=Next1(S,'='); Z:=S;
New(CPR);
CPR^.Name:=N;
CPR^.Data:=Z;
CE.ParamList.Add(CPR);
end else
//Дополнительные точки
if pos('Point(',TextSHA[I])‹›0 then
begin
Next1(S,'(');N:=Next1(S,')');
New(CPo);
Cpo^.Name:=N;
Cpo^.VName:='';
Cpo^.PointType:=-1; // Неопределино
// Переопредиляю ---------------------
If BaseElementDrawList‹›nil then begin
EI:=BaseElementDrawList.IndexOf( Ce.Name );
If Ei‹›-1 Then begin
BE:=TSElement(BaseElementDrawList.Objects[EI]);
IP:=IndexOfPointItem(N,BE.PointList);
If IP‹›-1 Then begin
BP:=BE.PointList[ip];
Cpo^.PointType:=BP^.PointType;
end
end
End;
//-----------------------------
Cpo^.Rem:='';
Cpo^.Option:='';
CE.PointList.Add(CPo);
{ }
end;
i:=i+1;
if I › TextSHA.Count-1 then break;
end;
i:=i+1;
EL.AddObject(ce.ID,CE);
if I › TextSHA.Count-1 then exit;
// Если есть контейнер ....
if (pos('BEGIN_SDK',TextSHA[I])‹›0) then
begin
Lev:=Lev+1;
I:=I+1;
//WRIteln('\\ BEGIN_SDK--------------');
CE.SubElementList:=TStringList.Create;
ParserOneLev(CE.SubElementList); //!! Рекурсия
if I › TextSHA.Count-1 then exit;
end else
// Конец контейнера
if (pos('END_SDK',TextSHA[I])‹›0) and (Lev›0) then
begin
Lev:=Lev-1;
MC:=MC+EL.Count;
//WRIteln('\\ END_SDK--------------');
I:=I+1;
exit; // Возварат на уровень выше
end;
//Application.ProcessMessages;
until I ›= TextSHA.Count-1;
end;
Var
ETB,ET,BE,CE: TSElement;
C,C2,CPo,BP: PSPoint;
begin
lev:=0; MC:=0;
Hed1:=TextSHA[0];
Hed2:=TextSHA[1];
I:=0; if ElementList=nil then ElementList:=TStringList.Create;
ElementList.Clear;
ParserOneLev( ElementList );
MC:=MC+ ElementList .Count;
{Добавляю точки из базы }
For I:=0 to ElementList.Count-1 do begin
ET:=TSElement(ElementList.Objects[i]);
N:=ET.Name;
K:=BaseElementDrawList.IndexOf(N);
if k=-1 then continue;
ETB:=TSElement(BaseElementDrawList.Objects[K]);
For J:=0 to etb.PointList.Count-1 do begin
C:= PSPoint(etb.PointList[j]);
K:= IndexOfPointItem(C^.name, et.PointList);
If (c^.Option‹›'/Hide') or (K‹›-1 )then Begin
New(C2);C2^:=C^;
C2^.Name:=C^.Name;
C2^.VName:=C^.VName;
C2^.Rem:=C^.REM;
// K:=IndexOfPointItem(C^.name^, et.PointList);
if K=-1 then Begin
C2^.Option:='';
Et.PointList.Add(C2);//.Insert(0,C2);
end else begin
C2^.Option:='';
C:= PSPoint(et.PointList[K]);
ClearPointItem(C);C^:=C2^;
end;
End;
end; //for etb.PointList
end;
end;
// Конвертор SHA 2 TXT
procedure SHA2TXT;
Var
s1,S,N:String;
COl,SX,SY,X1,Y1,X2,Y2,Cdo,Con,Cdat,Cvar,W,h,X,Y,K,I,J:Integer;
ET,ET2,ETB:TSElement;
C,C2: PSPoint;
L:PSLink;
//CK:TCanvas;CB:TBitmap;
Procedure ContactChip2d; // Контакты чипа
Var
J,YF,XF:Integer;
begin
For J:=0 to Cdo-1 do begin
Xf:=X;Yf:=y+Sy*J+6;
MiniSHA.Add(Format('s(%d,%d)' , [XF,YF]));
//CK.EllipseC(x,y+Sy*J+6,3,3);
end;
For J:=0 to Con-1 do begin
Xf:=x+W*SX;Yf:=y+Sy*J+6;
MiniSHA.Add(Format('s(%d,%d)' , [XF,YF]));
// CK.EllipseC(x+W*SX,y+Sy*J+6,3,3);
end;
For J:=0 to Cdat-1 do begin
Xf:=X+Sx*J+7;Yf:=y;
MiniSHA.Add(Format('s(%d,%d)' , [XF,YF]));
// CK.EllipseC(X+Sx*J+7,y,3,3);
end;
For J:=0 to Cvar-1 do begin
Xf:=X+Sx*J+7;Yf:=y+H*SY+SY;
MiniSHA.Add(Format('s(%d,%d)' , [XF,YF]));
//CK.EllipseC(X+Sx*J+7,y+H*SY,3,3);
end;
end;
Procedure Link2d; // Конверсия связей
Var
I,J,K:Integer;
begin
For I:=0 to ElementList.Count-1 do begin
ET:=TSElement(ElementList.Objects[i]);
//Link
if et.LinkList.Count›0 then
For J:=0 to et.LinkList.Count-1 do begin
L:=PSLink( et.LinkList[J]);
// Writeln (' [',L^.PointName^,']-›' );
// Writeln ('-› [',L^.PointTO^,'] ' );
K:=IndexOfPointItem(L^.PointName,et.PointList);
// Writeln (K);
If K=-1 then continue;
C:=PSPoint(et.PointList[k]);
K:= ElementList.IndexOf( L^.Id );
If K=-1 then continue;
ET2:=TsElement(ElementList.Objects[K]);
k:=IndexOfPointItem(L^.PointTO, et2.PointList);
C2:=PSPoint(et2.PointList[K]);
if C^.PointType in [3,4] then Col:=ClRed else Col:=clGreen;
if l^.LinkCP‹›'' then
begin
S:= l^.LinkCP;
X1:=C^.x-1; Y1:=C^.y-1; S:= l^.LinkCP; X2:= C2^.x-1;Y2:=C2^.y-1;
MiniSHA.Add(Format('p[%d]((%d,%d)',[col,x1,y1])+S+
Format('(%d,%d))',[x2,y2]));
end else
MiniSHA.Add(Format('p[%d]((%d,%d)(%d,%d))',[col,C^.x,C^.y-2,C2^.x,C2^.y-2]));
end;
end;
end;
Var
YF,XF,YF1,XF1:Integer;
TS:TStringList;
begin
// "на всякий пожарный"...
if BaseElementDrawList = nil then
begin
TS:=TStringList.Create;
CreateOrLoadElementBase('','Delphi.dat',false,TS);
ParserElementBase(TS);
//Memo2.Text:=cp1251toUtf8(Ts.Text);
ts.Free;
end;
///Button3Click(Sender);
if ElementList ‹› nil then ElementList.Clear;
parsing_sha.SHA_Parser(TextSHA);
//Button1Click(Sender);
if MiniSHA=nil then MiniSHA:=TStringList.Create;
if MiniSHA.Count ›0 then MiniSHA.Text:='';
Sx:=7; SY:=7;
// Тест формата
// для всех элементов схемы
For I:=0 to ElementList.Count-1 do begin
ET:=TSElement(ElementList.Objects[i]);
N:=ET.Name;
X:=ET.eX; Y:=ET.eY;
et.CalkMetrix;
Cdo:=ET.Cdo; Con:=ET.Con; CVar:=ET.CVar; CDat:=ET.CDat;
W:=et.WW; H:=et.HH;
Sx:=et.StepX; Sy:=et.StepY;
// "Чип"
X:=ET.eX; Y:=ET.eY;
X2:=x+(w*Sx);Y2:= y+(h*Sy)+Sy;
MiniSHA.Add(Format('c(%d,%d,%d,%d)',[x,y,x2,y2]));
// контакты "Чипа"
ContactChip2d;
end;//for ElementList
Link2d; // Конверсия связей
/// MiniSHA.SaveToFile('MiniSHA.Txt');
end;
end.
Редактировалось 14 раз(а), последний 2021-07-26 14:06:55