Chủ Nhật, 31 tháng 8, 2025

Các câu lệnh thường dùng trong mp-geom2d

Viết các câu lệnh này vào macro của Unikey, Script của TeXWorks hoặc vào Tag User của TeXMaker, thậm chi vào macro của Nodepad++ để gọi vào TeXEditor.

\documentclass[border=5mm,12pt]{standalone}
\usepackage{unicode-math}
\setmathfont[Extension =.otf,BoldFont = XITSMath-Bold]{XITSMath-Regular}
\usepackage{luamplib}
\begin{document}
\mplibtextextlabel{enable}
\begin{mplibcode}
input geom2d;

beginfig(1);

% Viết nội dung vào đây

%Thiết lập khung hiển thị
Window(-1,-4,12,10);
FrameMinMax(0, 12, 0, 10, 1, 1);
drawoptions(withcolor LightGrey);
FrameGrid;
endfig;
\end{mplibcode}
\end{document}


drawoptions(withpen pencircle scaled 1pt withcolor black);
A = Point(5,7); 
B = Point(1,2); 
C = Point(7,2);
gddMark.top "A"; gddMark.llft "B"; gddMark.lrt "C" ;
ABC = Triangle(A,B,C); gddDraw ABC;
AB = Line(A, B); AC =  Line(A, C); BC = Line(B, C); 
 
dtrngtiep_ = CircumscribedCircle(ABC); %gddDraw dtrngtiep_;
tamngtiep_ = Center(dtrngtiep_); %gddMark.lft "tamngt_";
R = Radius(dtrngtiep_);
Axtd = CentralSymmetry(A,tamngtiep_); %gddMark.bot "Axtd";
 
 
tructam_= Orthocenter(ABC); %gddMark.rt "tructam_"; 
Atructam_= Line(A, tructam_); 
cdcA = LinesIntersection(BC,Atructam_); %gddMark.bot "cdcA";
 
trongtam_ = IsoBarycenter(A,B,C); %gddMark.rt "trongtam_"; 
Atrongtam_ = Line(A, trongtam_); 
trdBC = LinesIntersection(BC,Atrongtam_); %gddMark.bot "trdBC";
%trdbc = MidPoint(B,C); %gddMark.bot "trdbc";
 
dtrntieP_ = InscribedCircle(ABC); %gddDraw dtrnt_;
tamntiep_ = Center(dtrntiep_); %gddMark.bot "tamntiep";
cpgtA = LinesIntersection(Line(A,tamntiep_),BC); 
dpgtA = AngleBisector(B,A,C);

 
 
gddDraw OrthoSign(A,H,C,0.5); % vẽ dấu góc vuông
B_AM = ParallelLine(Line(A,M),B); gddDraw B_AM;
B_AM = PerpendicularLine(Line(A,M),B); gddDraw B_AM;
 
T = LineCircleIntersection(Line(M,H),dtrngt,1); gddMark.lft "T";
T = LinesIntersection(Line(A,B),Line(C,D)); gddMark.lft "T";
T = CirclesIntersection(c_1,c_2); gddMark.lft "T";
 
%Tiếp tuyến kẻ từ 1 diểm nằm ngoài đường tròn
pointA ; dtrC ; 
tamdtrC = Center(dtrC);
tiepdiemm  = CirclesIntersection(dtrC, CircleD(pointA,tamdtrC));
tiepdiemh = CirclesIntersection(CircleD(pointA,tamdtrC),dtrC);
gddDraw Segment(pointA,tiepdiemm);
gddDraw Segment(pointA,tiepdiemh);
 
 
    
% Khai báo hệ trục tọa độ ngay sau beginfig và trước endfig
Frame(9,9,4,5,0.8,0.8);
Axis;
	gddBegin;
	Axis;
	Graduations;
	Units(1);
	nội dung
	gddEnd;


Hàm invert.mp

% Hàm tính ảnh đường tròn qua phép nghịch đảo
vardef invert_circle(expr input_circle, inversion_circle) =
    save cen, r, xcen, ycen, X, Y, Z, iX, iY, iZ;
    cen = Center(input_circle);
    r = Radius(input_circle);
    xcen = Xcoordinate(cen);
    ycen = Ycoordinate(cen);
    
    % Chọn 3 điểm trên đường tròn (cách đều 120 độ)
    X = Point(xcen + r*cos(0), ycen + r*sin(0));
    Y = Point(xcen + r*cos(120), ycen + r*sin(120));
    Z = Point(xcen + r*cos(240), ycen + r*sin(240));
    
    % Tính ảnh của 3 điểm qua phép nghịch đảo
    iX = Inverse(X, inversion_circle);
    iY = Inverse(Y, inversion_circle);
    iZ = Inverse(Z, inversion_circle);
    
    % Tạo đường tròn mới từ 3 điểm ảnh
    CircleThreePoints(iX, iY, iZ)

 

Thứ Sáu, 29 tháng 8, 2025

Tạo file clock9.gif đồng hồ chạy từ 9h00 đến 9h59

  1. Viết một file metapost như sau, lưu thành file clock9.mp :

    path hand[];
    hand1 = origin .. (.257,1/50) .. (.377,1/60)
    & (.377,1/60) {up} .. (.40,3/50)
    .. (.60, 1/40) .. {right} (.75,0);
    hand1 := (hand1 .. reverse hand1 reflectedabout(left,right)
    .. cycle) scaled 50;
    hand2 = origin .. (.60, 1/64) .. {right} (.925,0);
    hand2 := (hand2 .. reverse hand2 reflectedabout(left,right)
    .. cycle) scaled 50;
    % hour of the day to degrees
    vardef htod(expr hours) = 30*((15-hours) mod 12) enddef;
    vardef mtod(expr minutes) = 6*((75-minutes) mod 60) enddef;
    vardef clock(expr hours, minutes) = image(
    % face and outer ring
    fill fullcircle scaled 100 withcolor 1/256(240, 240, 230);
    draw fullcircle scaled 99 withcolor .8 white;
    draw fullcircle scaled 100 withpen pencircle scaled 7/8;
    % hour and minute marks
    for t=0 step 6 until 359:
    draw ((48,0)--(49,0)) rotated t;
    endfor
    for t=0 step 30 until 359:
    draw ((47,0)--(49,0)) rotated t withpen pencircle scaled 7/8;
    endfor
    % numerals
    for h=1 upto 12:
    label(decimal h infont "bchr8r", (40,0) rotated htod(h));
    endfor
    % hands rotated to the given time
    pickup pencircle scaled 7/8;
    filldraw hand1 rotated htod(hours+minutes/60);
    filldraw hand2 rotated mtod(minutes);
    % draw the center on top
    draw origin withpen pencircle scaled 5;
    undraw origin withpen pencircle scaled 3;
    ) 
    enddef;
    
    % Tạo 60 hình từ 0 đến 59, mỗi hình trên một trang riêng
    for i = 0 upto 59:
      beginfig(i);
        draw clock(9, i); % 9 giờ, i phút
      endfig;
    endfor
    end
    

  2. Mở một Terminal, chuyển đến thư mục làm việc là thư mục chứa file mp nói trên. Tại dòng lệnh ta gõ:
    mptopdf clock9.mp
    nhấn Enter, metapost sẽ biên dịch thành 60 file pdf đặt tên là clock9-0.pdf, clock9-1.pdf, ... , clock9-59.pdf
  3. Viết một file $\rm \TeX$ đặt tên là clock9.tex như sau:

    \documentclass{minimal}
    \usepackage{graphicx}
    \usepackage{multido} 
    \usepackage[paperwidth=8cm,paperheight=8cm,
    top=0.3cm,bottom=0cm,left=-.3cm,right=0cm]{geometry} 
    \DeclareGraphicsRule{*}{mps}{*}{}
    \begin{document} 
    \multido{\n=0+1}{60}{\includegraphics[scale=2]{clock9-\n.pdf} \newpage }
    \end{document}
    Biên dịch bằng PdfLaTeX ta được file clock9.pdf. Để không bị rối xóa tất cả file có dạng clock9-*.pdf
  4. Dùng GIMP mở file clock9.pdf, export to clock9.gif , chọn tần số là 60000 (mili giây).
Đồng hồ chạy lúc 9g, kim phút và kim giờ sẽ chạy sau mỗi 15 giây. Xem chỉnh sửa file mp và file $\rm \TeX$ ở dưới.

filldraw hand1 rotated htod(hours+minutes/240) withcolor red;
filldraw hand2 rotated mtod(minutes/4) withcolor blue;

Edit file $\rm Lua\LaTeX$
\multido{\n=0+1}{240}{\includegraphics[scale=2]{dh9giay-\n.pdf} \newpage }

Thứ Tư, 27 tháng 8, 2025

Phép nghịch đảo với mp-geom2d

Trong gói mp-geom2d có toàn bộ công cụ vẽ hình học phẳng, đặc biệt tích hợp sẵn phép nghịch đảo (Inverse). Ta sử dụng chúng như sau:
  1. Tạo một file $\rm Lua\LaTeX$:
    \documentclass[border=5mm]{standalone}
    \usepackage{luamplib}
    \begin{document}
    \mplibtextextlabel{enable}
    \begin{mplibcode}
    input geom2d;
     beginfig(1);
     % nội dung 
     Window(-1,-1,5.5,6);
    endfig;
    \end{mplibcode}
    \end{document}
        
  2. Ảnh của một điểm. Ta xác định một phép nghịch đảo cực $A(2;2)$, đường tròn nghịch đảo là đường tròn tâm $A$ bán kính $2$, nghĩa là phương tích nghịch đảo $k=R^2=4$. Viết đoạn code sau đây ngay sau beginfig(1);
    A = Point(2,2);
    C = Circle(A,2);
    gddDraw C withcolor Cyan withpen pencircle scaled 1bp;
    gddMark.rt "A";
    d = Segment(A,B);
    gddDraw d withcolor Gray withpen pencircle scaled 1bp;
     
    Biên dịch bằng LuaLaTeX có kết quả như ở dưới. Phần phía trên của hình vẽ còn trống: là do ta chọn Window(-1,-1,5.5,6) để còn có thể vẽ thêm hình vào file này.
  3. Ảnh của môt đường tròn.
  4. Ta biết rằng một đường tròn không đi qua cực nghịch đảo sẽ biến thành một đường tròn. Tâm của đường tròn nguồn không biến thành tâm của đường tròn ảnh, nhưng hai tâm thẳng hàng với cực nghịch đảo.

    Rất tiếc cũng giống như pstricks, phép nghịch đảo của mp-geom2d không biến được đường tròn thành đường tròn một cách chính xác.
    E = Point(3,1);
    CE = Circle(E,0.5);
    gddDraw CE  withcolor Gray withpen pencircle scaled 1bp;
    gddMark.rt "E";
    iCE = Inverse(CE,C);
    F = Center(iCE);
    gddMark.rt "F";
    gddDraw iCE  withcolor red withpen pencircle scaled 1.5bp;


    Hình vẽ dưới đây phép nghich đảo biến đường tròn $(E)$ xám (Gray) thành đường tròn đỏ $(F)$ , nhưng đó không là ảnh của đường tròn $(E)$. Nguyên nhân có thể dự đoán được là nó lấy một tập hợp hữu hạn các điểm của đường tròn $(E)$, lấy ảnh của từng điểm rồi nối lại, kết quả nối sai.

    Sau hình vẽ ở dưới là cách khắc phục.

    Khắc phục.
    • Viết một hàm đặt trong một file metapost (ví dụ invert.mp ) xác định ảnh của đường tròn qua phép nghịch đảo, tất nhiên đường tròn nguồn không đi qua cực.
      % Hàm tính ảnh đường tròn qua phép nghịch đảo
      vardef invert_circle(expr input_circle, inversion_circle) =
          save cen, r, xcen, ycen, X, Y, Z, iX, iY, iZ;
          cen = Center(input_circle);
          r = Radius(input_circle);
          xcen = Xcoordinate(cen);
          ycen = Ycoordinate(cen);
          
          % Chọn 3 điểm trên đường tròn (cách đều 120 độ)
          X = Point(xcen + r*cos(0), ycen + r*sin(0));
          Y = Point(xcen + r*cos(120), ycen + r*sin(120));
          Z = Point(xcen + r*cos(240), ycen + r*sin(240));
          
          % Tính ảnh của 3 điểm qua phép nghịch đảo
          iX = Inverse(X, inversion_circle);
          iY = Inverse(Y, inversion_circle);
          iZ = Inverse(Z, inversion_circle);
          
          % Tạo đường tròn mới từ 3 điểm ảnh
          CircleThreePoints(iX, iY, iZ)
      enddef;
      
    • Sau đây là file $\rm \TeX$ sử dụng mp-geom2d.mpinvert.mp

      \documentclass[border=5mm]{standalone}
      \usepackage{luamplib}
      \begin{document}
      \mplibtextextlabel{enable}
      \begin{mplibcode}
      input geom2d;
      input invert;
      
      beginfig(1);
      
      % Đường tròn nghịch đảo
      A = Point(2,4);
      C = Circle(A,2);
      gddDraw C withcolor Cyan withpen pencircle scaled 1bp;
      gddMark.rt "A";
      
      % Vẽ nhiều đường tròn và ảnh nghịch đảo của chúng, 
      % chọn upto 0 để lấy 1 đường tròn.  
      for i = 0 upto 0:
      save E, CE, invCE, F;  % Khai báo biến cục bộ
      E = Point(3 + i*0.7, 3 + i*0.3);
      CE = Circle(E, 0.5 + i*0.1);
          
      % Vẽ đường tròn gốc
      gddDraw CE withcolor (0.2, 0.6, 0.2) withpen pencircle scaled 1bp;
      gddMark.rt "E";
          
      % Tính và vẽ ảnh nghịch đảo
      invCE = invert_circle(CE, C);
      gddDraw invCE withcolor (0.8, 0.2, 0.2) withpen pencircle scaled 1bp;
          
      % Đánh dấu tâm đường tròn ảnh
      F = Center(invCE);
      gddMark.rt "F";
      endfor
      
      % Thiết lập khung hiển thị
      FrameMinMax(0, 6, 0, 6, 1, 1);
      drawoptions(withcolor LightGrey);
      FrameGrid;
      
      endfig;
      \end{mplibcode}
      \end{document}


    • Kết quả chính xác:
  5. Ảnh của đường thẳng. Qua phép nghịch đảo một đường thẳng không đi qua cực nghịch đảo sẽ biến thành một đường tròn đi qua cực nghịch đảo. Vì phép nghịch đảo có tính chất đối hợp nên một đường tròn đi qua cực nghịch đảo sẽ biến thành một đường thẳng.
    d = Line((3,3),(4,2));
    gddDraw d withcolor Gray withpen pencircle scaled 1bp;;
    Cd = Inverse(d,C);
    gddDraw Cd withcolor Red withpen pencircle scaled 1.5bp;


THỰC HÀNH


  1. Tạo một file $\rm Lua\LaTeX$, khai báo ba điểm $A, B, C$ có tọa độ như ghi trong file.
    \documentclass[border=5mm]{standalone}
    \usepackage{luamplib}
    \begin{document}
    \mplibtextextlabel{enable}
    \begin{mplibcode}
    input geom2d;
    input invert;
    beginfig(1);
    d = 10;
    A = Point(0,0);
    C = Point(d,0);
    B= Point(3*d/4,0); 
    % nội dung sẽ viết ở đây
    Window(-1,-14,14,14);
    % Thiết lập khung hiển thị
    FrameMinMax(0, 18, 0, 20, 1, 1);
    drawoptions(withcolor LightGrey);
    FrameGrid;
    endfig;
    \end{mplibcode}
    \end{document}
  2. Viết vào sau dòng 12:
    gddMark.lft "A";
    gddMark.llft "B";
    gddMark.rt "C";
    gddDraw Segment(A,C) withcolor Cyan withpen pencircle scaled 1bp;
    U = CircleCP(A,C);
    gddDraw U withcolor Cyan withpen pencircle scaled 1bp;
    X = CircleD(A,B);
    gddDraw X withcolor Gray withpen pencircle scaled 1bp;
    Y = CircleD(A,C);
    gddDraw Y withcolor Gray withpen pencircle scaled 1bp;

    3 dòng đầu tiên hiển thị các điểm $A, B, C$.
    Dòng thứ 4 vẽ đoạn thẳng $AC$.
    Dòng thứ 5 và dòng thứ 6 xác định và vẽ đường tròn tâm $A$ và đi qua $C$, chính là đường tròn nghich đảo, chọn màu rất nhạt.
    Dòng thứ 7 và dòng thứ 8 xác định và vẽ đường tròn đường kính $AB$.
    Dòng thứ 9 và dòng thứ 10 xác định và vẽ đường tròn đường kính $AC$.

  3. Bây giờ ta bắt đầu sẽ chuỗi đường tròn Pappus. Trước hết ta vẽ đường tròn chuẩn, đó là đường tròn đường kính $BC$

    Thêm dòng code
    Z := CircleD(B,C);
    gddDraw Z withcolor Gray withpen pencircle scaled 1bp;

  4. Dùng phép nghịch đảo cực $A$, phương tích $k=AC^2$, tức là đường tròn nghịch đảo $U$ là đường tròn bán kính $AC$. Dùng phép nghich đảo này biến đường tròn các đường kính $AB$ và $AC$ đi qua cực nghịch đảo và tiếp xúc nhau thành thành các đường thẳng song song.

    Thêm 4 dòng lệnh
    iX = Inverse(X,U);
    gddDraw iX withcolor Gray withpen pencircle scaled 1.5bp;
    iY = Inverse(Y,U);
    gddDraw iY withcolor red withpen pencircle scaled 1.5bp;
  5. Không dùng phép nghịch đảo nói trên để tìm ảnh của đường tròn đường kính $BC$. Thay vào đó ta dùng phép nghịch đảo invert_circle trong file invert.mp để biến đường tròn đường kính $BC$ thành đường tròn, ta tạm gọi là (bleu). Đường tròn (blue) tiếp xúc với hai đường thẳng song song vì tạo ảnh của nó tiếp xúc với hai đường tròn $[AB]$ và $[AC]$.

    Thêm câu lệnh:
    invZ := invert_circle(Z, U);
    gddDraw invZ withcolor blue withpen pencircle scaled 1.5bp;
    D = Center(invZ);
    gddMark.bot "D";
    Rp = Radius(invZ);

    Dòng 1 và dòng 2 xác định và vẽ đường tròn nghịch đảo của đường tròn $[BC]$.
    Dòng 3 và dòng 4 xác định và hiển thị tâm, dòng 5 xác định bán kính của đường tròn nghịch đảo đó.
  6. Ta tịnh tiến tâm $D$ của đường tròn (blue) theo vectơ $(0,2*i*Rp)$ thành các điểm $E[i]$. Sau đó lấy $E[i]$ làm tâm và bán kính bằng bán kính của đường tròn (blue) để vẽ các đường tròn $C[i]$ tịnh tiến của đường tròn (blue).

    Thêm dòng code:
    for i:=-18 upto 18:
    E[i] = Addition(D,Point(0,2*i*Rp));
    C[i] := Circle(E[i],Rp);
    gddDraw C[i]  withcolor blue withpen pencircle scaled 1.5bp;
    endfor
  7. Dùng phép nghịch đảo invert_circle để xác định ảnh của các đường tròn $C[i]$ thành các đường tròn $D[i]$. Sau đó với 8 đường tròn xung quang (blue), hiển thị tâm của chúng, nối các đoạn thẳng đi từ cực nghich đảo $A$ đên các tâm của $C[i]$.

    Thêm dòng code:
    D[i] := invert_circle(C[i],U);
    gddDraw D[i]  withcolor red withpen pencircle scaled 1.5bp;


    sang vòng lặp khác để vẽ các đoạn thẳng $AC[i]$:

    for i:=-4 upto 4:
    T[i]  := Center(C[i]); gddDrawPoint T[i];
    K[i]  := Center(D[i]); gddDrawPoint K[i];
    gddDraw Segment(A,T[i])  withcolor LightGray withpen pencircle scaled  1bp;
    endfor


Code hoàn chỉnh của chuỗi đường tròn Pappus vẽ bằng mp-geom2d. Cần phải có 2 file geom2d.mp (thực ra là một thư mục) và file invert.mp

\documentclass[border=5mm]{standalone}
\usepackage{luamplib}
\begin{document}
\mplibtextextlabel{enable}
\begin{mplibcode}
input geom2d;
input invert;
beginfig(1);
d = 10;
A = Point(0,0);
C = Point(d,0);
B= Point(3*d/4,0); 
gddMark.lft "A";
gddMark.llft "B";
gddMark.rt "C";
gddDraw Segment(A,C) withcolor Cyan withpen pencircle scaled 1bp;
U = CircleCP(A,C);
gddDraw U withcolor Cyan withpen pencircle scaled 1bp;
X = CircleD(A,B);
gddDraw X withcolor Gray withpen pencircle scaled 1bp;
Y = CircleD(A,C);
gddDraw Y withcolor Gray withpen pencircle scaled 1bp;
iX = Inverse(X,U);
gddDraw iX withcolor Gray withpen pencircle scaled 1.5bp;
iY = Inverse(Y,U);
gddDraw iY withcolor red withpen pencircle scaled 1.5bp;
Z := CircleD(B,C);
gddDraw Z withcolor Gray withpen pencircle scaled 1bp;
invZ := invert_circle(Z, U);
gddDraw invZ withcolor blue withpen pencircle scaled 1.5bp;
D = Center(invZ);
gddMark.bot "D";
Rp = Radius(invZ);
for i:=-18 upto 18:
E[i] = Addition(D,Point(0,2*i*Rp));
C[i] := Circle(E[i],Rp);
gddDraw C[i]  withcolor blue withpen pencircle scaled 1.5bp;
D[i] := invert_circle(C[i],U);
gddDraw D[i]  withcolor red withpen pencircle scaled 1.5bp;
endfor
for i:=-4 upto 4:
T[i]  := Center(C[i]); gddDrawPoint T[i];
K[i]  := Center(D[i]); gddDrawPoint K[i];
gddDraw Segment(A,T[i])  withcolor LightGray withpen pencircle scaled
 1bp;
endfor
Window(-1,-14,14,14);
% Thiết lập khung hiển thị
FrameMinMax(0, 18, 0, 20, 1, 1);
drawoptions(withcolor LightGrey);
FrameGrid;
endfig;
\end{mplibcode}
\end{document}

Thứ Ba, 26 tháng 8, 2025

Template metapost

\documentclass[border=5mm]{standalone}
\usepackage{luamplib}
\begin{document}
\mplibtextextlabel{enable}
\begin{mplibcode}
% nội dung của file mp
\end{mplibcode}
\end{document}

 

Đồng hồ giờ phút bằng metapost

path hand[];
hand1 = origin .. (.257,1/50) .. (.377,1/60)
& (.377,1/60) {up} .. (.40,3/50)
.. (.60, 1/40) .. {right} (.75,0);
hand1 := (hand1 .. reverse hand1 reflectedabout(left,right)
.. cycle) scaled 50;
hand2 = origin .. (.60, 1/64) .. {right} (.925,0);
hand2 := (hand2 .. reverse hand2 reflectedabout(left,right)
.. cycle) scaled 50;
% hour of the day to degrees
vardef htod(expr hours) = 30*((15-hours) mod 12) enddef;
vardef mtod(expr minutes) = 6*((75-minutes) mod 60) enddef;
vardef clock(expr hours, minutes) = image(
% face and outer ring
fill fullcircle scaled 100 withcolor 1/256(240, 240, 230);
draw fullcircle scaled 99 withcolor .8 white;
draw fullcircle scaled 100 withpen pencircle scaled 7/8;
% hour and minute marks
for t=0 step 6 until 359:
draw ((48,0)--(49,0)) rotated t;
endfor
for t=0 step 30 until 359:
draw ((47,0)--(49,0)) rotated t withpen pencircle scaled 7/8;
endfor
% numerals
for h=1 upto 12:
label(decimal h infont "bchr8r", (40,0) rotated htod(h));
endfor
% hands rotated to the given time
pickup pencircle scaled 7/8;
filldraw hand1 rotated htod(hours+minutes/60);
filldraw hand2 rotated mtod(minutes);
% draw the center on top
draw origin withpen pencircle scaled 5;
undraw origin withpen pencircle scaled 3;
) 
enddef;

beginfig(1); 
draw clock(4, 12); 
endfig;
end