Tất cả các thay đổi đáng chú ý của dự án HeyGeo được ghi lại theo thứ tự thời gian ngược (mới nhất trên cùng).
Định dạng: [YYYY-MM-DD HH:MM]
Căn cứ theo múi giờ ICT (UTC+7) — Việt Nam.
- Vấn đề: Tất cả đo lường không thuộc hệ tọa độ (distance, length, radius, angle, ratio, slope) xuất ra văn bản tĩnh. Khi kéo điểm, giá trị không cập nhật.
- Sửa: Thêm helper
measureNumExprJs()tạo biểu thức JS động dùngboard.objects[...].X()/.Y()/.Radius()/.point1/.point2cho từng kiểu đo lường. WrappermeasureToJsFunc()đóng gói thànhfunction(){return 'AB = '+expr.toFixed(2)+' cm';}để JSXGraph gọi tự động mỗi lần board cập nhật.
- Vấn đề:
displayTextcủaG_calculateObjectchỉ chứa giá trị số ("2.36"), không có nhãn công thức ("AB + CD = 2.36 cm"). - Sửa: (1) Thêm helper
expandCalcLabel()khai triển chuỗi công thức (REFERENCE_NUM chars → parent labels) để tạo nhãn đọc được. (2) Mở rộngcalcFormulaToJs()nhận thêm tham sốcmFvà xử lý cả parentG_MEASURE(dùngmeasureNumExprJs()). (3) Static fallback hiển thị"formula = value"thay vì chỉ"value".
- Hệ tọa độ: JSXGraph dùng y hướng lên, HeyGeo dùng y hướng xuống. Góc chuyển đổi:
atan2(-dy_jsx, dx)thay gốcatan2(dy_screen, dx). cmF = SCALE * G_cmPerPixel()(cm/JSXGraph unit) nhúng như hằng số export-time.- Angle formula:
((-(a2-a0)+4π)%(2π))*180/πkhớp với desktopfmod(-value+4π, 2π)*180/π. - Slope formula:
dy_jsx/dx_jsx= độ dốc toán học = giá trị desktop (y-flip triệt tiêu dấu âm trướcp.getY()).
src/app/HeyGeoWebExporter.cpp— Thay thếcalcFormulaToJs()(mở rộng hỗ trợ G_MEASURE parents); thêmexpandCalcLabel(),measureNumExprJs(),measureToJsFunc(); tái cấu trúccase G_MEASURE: case G_CALCULATE:trongappendElements().
- Vấn đề: Display → Color không đổi màu biểu thức đo lường / tính toán trên sketch.
G_valueObject::draw()đặt màu pen đúng nhưngKFormula::redraw()luôn dùngforeColornội bộ (mặc định đen) khi vẽ từngbox, ghi đè lại màu đó. - Sửa: Trong
G_valueObject::draw()(src/geometry/G_measureObject.cpp), gọitext.setForeColor(drawColor)trướctext.redraw(p)đểKFormulatheo màu drawstyle của ref.
- Vấn đề: Khi dùng một
G_calculateObject(ví dụ:AD·AB = 37.24 cm²) làm tham chiếu trong công thức kế tiếp,getUnitType()trả về dimensionless vì chỉ kiểm traG_MEASURE. Hệ quả:AD·AB / 1cmhiển thị đơn vịcmthay vìcm²/cm = cm. - Sửa: Thêm nhánh
G_CALCULATEvào cả haiCalculationReferenceFetcher(G_calculateObject.cpp) vàCalculationDialogReferenceFetcher(HeyGeoCalculateEditor.cpp): nếu ref làG_CALCULATE, gọigetLhs()->evaluateUnit()để lấy đúng kiểu đơn vị của công thức con.
src/geometry/G_measureObject.cpp—G_valueObject::draw(): đồng bộforeColorvới drawstyle.src/geometry/G_calculateObject.cpp—CalculationReferenceFetcher::getUnitType(): xử lýG_CALCULATE.src/app/HeyGeoCalculateEditor.cpp—CalculationDialogReferenceFetcher::getUnitType(): xử lýG_CALCULATE.
- Vấn đề:
evaluateUnit()trongformula/kformula.cpp, caseCAT(implicit multiplication), kiểm traif (!b->b1 || b->b1->type != TEXT)trước khi tra tên hàm. TEXT node"1"(chữ số người dùng nhập) thoả điều kiện TEXT, bị xử lý như tên hàm, không khớp tên nào → trả về dimensionless. Kết quả:1cm= CAT(TEXT"1", TEXT"cm") → dimensionless, khiếnAD / 1cm= L − 0 = L (có đơn vị cm thay vì vô đơn vị). - Sửa: Đảo logic — kiểm tra xem b1 có phải tên hàm đã biết không; nếu không → tính như phép nhân ngầm giữa hai toán hạng (
{u1.L + u2.L, u1.A + u2.A}).
formula/kformula.cpp—KFormula::evaluateUnit(), caseCAT: cấu trúc lại logic phân biệt function call vs. implicit multiplication.
- Thêm nút Units ▼ vào cột trái của Calculator dialog, ngay dưới "Functions ▼".
- Menu gồm: cm · in · px (phân cách) · ° · rad — click chèn token đơn vị vào vị trí con trỏ công thức.
- Đơn vị hoạt động như hằng số nhân:
5 in= 5 × 2.54 = 12.7 cm;2 rad= 2 × (180/π) = 114.59°.
evaluateUnit()nhận diện TEXT node "cm"/"in"/"px" →UnitType::length(); "deg"/"rad" →UnitType::angle().- Cho phép kiểm tra chiều tự động:
AD / 1cm→ dimensionless;5 in + 3 cm→ length;AD * AB→ cm².
- Cả
G_calculateObject::calculate()(sketch) vàupdateValue()(live preview trong dialog) đều truyền hash{"cm":1, "in":2.54, "px":G_cmPerPixel(), "deg":1, "rad":180/π}vàoevaluate().
- Padding hai bên toán tử PLUS / MINUS trong
formula/box.cpptăng từ 1px/3px lênSPACE(4px) đồng đều cả hai phía.
src/app/HeyGeoCalculateEditor.H— thêmunitsBtn,showUnitsMenu(),buildUnitsMenu().src/app/HeyGeoCalculateEditor.cpp— nút Units,buildUnitsMenu(),showUnitsMenu(); unit vars trongupdateValue();getUnitType()xử lýG_CALCULATE.src/geometry/G_calculateObject.cpp—calculate(): truyền unit vars vàoevaluate().src/geometry/G_measureObject.cpp—G_cmPerPixel(): bỏstatic, expose ra ngoài.src/geometry/G_object.H— khai báodouble G_cmPerPixel().formula/kformula.cpp—evaluateUnit(): nhận diện token đơn vị cho TEXT node.formula/box.cpp— PLUS/MINUS padding: 1px/3px → 4px/4px.
Khi đang dùng các công cụ dựng hình (Point, Segment, Ray, Line, Compass, Polygon…), di chuột đến gần đối tượng sẽ kích hoạt hiệu ứng highlight kiểu GSP:
- Đối tượng được snap tới sáng lên màu đỏ tươi với độ dày nét tăng nhẹ.
- Shadow mờ dần (soft glow) bao quanh từng loại đối tượng:
- Điểm:
QRadialGradienttạo vành halo tối đỉnh tại cạnh điểm, mờ dần vào trong và ra ngoài. - Đường thẳng / đoạn / tia: 3 stroke pass chồng nhau (rộng→hẹp, mờ→đậm) tạo gradient bao quanh hai bên.
- Đường tròn: tương tự, halo bên trong và bên ngoài vòng tròn.
- Điểm:
- Giao điểm hai đường: khi con trỏ gần giao của hai đường (thẳng hoặc tròn), cả hai sáng lên đồng thời, kèm hình thoi nhỏ ◇ chỉ vị trí giao dự kiến.
- Điểm luôn nằm trước: sau khi vẽ highlight, toàn bộ điểm hiển thị được vẽ lại lên trên cùng để không bị nét đỏ che khuất (đúng thứ tự z-order của GSP).
src/app/HeyGeoView.H- Thêm 3 thành viên:
toolHoveredRefs(G_refs),toolHoveredIsIntersection(bool), khai báodrawToolHoverHighlight(QPainter &).
- Thêm 3 thành viên:
src/app/HeyGeoView.cpp- Khởi tạo
toolHoveredIsIntersection = falsetrong constructor. mouseMoveEvent(): phát hiện snap target (điểm ưu tiên, sau đó curve đơn, rồi hai curve → intersection), lưu vàotoolHoveredRefs, triggerrepaint()khi thay đổi.paintEvent(): gọidrawToolHoverHighlight(p)saudrawToolRubberband(p).setToolMode()/cancelCurrentTool(): xóatoolHoveredRefskhi đổi/huỷ tool.- Thêm hàm
drawToolHoverHighlight()với soft-glow multi-pass rendering.
- Khởi tạo
src/app/HeyGeoDocument.H- Thêm accessor
getAllRefs()đểHeyGeoViewvẽ lại điểm sau highlight.
- Thêm accessor
Sau khi dựng cung tròn qua 3 điểm (Construct → Arc through 3 Points), cung không select được khi click vào, dẫn đến không thể thực hiện bất kỳ phép dựng nào tiếp theo (Center Point, Endpoints, Arc Sector, Intersection...).
Trong G_arc::getExtents() (src/geometry/G_arc.cpp), hai trường hợp tính giá trị tối đa của bounding box sử dụng C++ comma operator thay vì qMax():
// Lỗi — dùng comma operator: C++ bỏ e1, chỉ gán e2
else x2 = (e1.getX(), e2.getX());
else y2 = (e1.getY(), e2.getY());Comma operator làm cho bounding box bị tính sai (rất nhỏ hoặc lệch), khiến getSelectExtents().contains(click_point) trong whatAmIOn() trả về false → click không nhận ra arc → arc không select được.
Thay comma operator bằng qMax():
else x2 = qMax(e1.getX(), e2.getX());
else y2 = qMax(e1.getY(), e2.getY());src/geometry/G_arc.cpp—G_arc::getExtents(): sửa hai dòng dùng comma operator thànhqMax().
Graph → Grid Formtrở thành submenu với 4 lựa chọn checkable (radio-style):- Polar Grid — vòng tròn đồng tâm (bán kính nguyên) + 12 đường xuyên tâm mỗi 30°; nhãn bán kính cạnh phải.
- Square Grid (mặc định) — tỷ lệ x/y bằng nhau, tick và lưới vuông vắn như GSP.
- Rectangular Grid — tỷ lệ x/y độc lập; điểm V (0,1) hiển thị để kéo lên/xuống đổi đơn vị trục Oy.
- Trigonometric Axis — trục x nhãn bội số π (π/6, π/4, ..., 2π); trục y số thực thông thường.
- Mục cũ "Grid Form" (toggle grid) đổi tên thành "Hide/Show Grid" và giữ chức năng.
- Kiểu điểm ràng buộc mới, đối xứng với
G_HORIZONTAL_POINT: luôn bám O theo phương dọc (x = parent.x),dy ≥ 10 px. - Tạo tự động khi
New Coordinate System, ẩn hoàn toàn theo mặc định (Square Grid). - Chỉ hiện khi chuyển sang Rectangular Grid; ẩn lại khi bỏ chế độ đó.
G_COORDSYSnhận V làmpar[2];update()tínhscaleY = dist(O, V)để vẽ tick trục y độc lập.
- Mỗi hệ tọa độ mới gồm 2 đường
G_LINE(xAxis = O→U, yAxis = O→V). - Có thể chọn (Select) và dựng điểm trên trục bằng Point Tool như mọi G_LINE khác.
G_coordsys::draw()chỉ vẽ đầu mũi tên + tick + nhãn; đường trục doG_LINEtự vẽ.
src/defs.H— thêmG_VERTICAL_POINTvàoenum G_PointTypesrc/geometry/G_coordsys.H— thêmenum GridForm, fieldm_scaleY,m_gridForm, cập nhật APIsrc/geometry/G_coordsys.cpp—draw(): dispatch theo GridForm;save()/load(): thêmm_scaleY,m_gridFormsrc/geometry/G_graphObjects.cpp—update(): tínhscaleYtừpar[2]khi RECTANGULAR_GRIDsrc/geometry/G_pointObject.cpp—update()/save()/load(): caseG_VERTICAL_POINTsrc/geometry/G_object.H—resetP(): thêmG_VERTICAL_POINTsrc/app/BuildReverse.cpp+HeyGeoView.cpp— drag: nhận diệnG_VERTICAL_POINTsrc/app/menu.cpp—graphNewCoordSys(): tạo V (ẩn, par[2]); 4 handlergraphGridFormXxx();applyGridForm()show/hide Vsrc/app/HeyGeoDocument.H— khai báogetCurrentGridForm(), 4 slotgraphGridFormXxx()src/app/HeyGeoMenus.cpp— classGridFormInfo+HeyGeoGridFormMenuMaintainer; thay PCInfo "Grid Form" bằng submenu + "Hide/Show Grid"
Khi export sang JSXGraph (HeyGeo Web), quỹ tích (G_LOCUS) bị mất hoặc hiển thị sai:
- Tất cả 150 điểm snapshot có cùng tọa độ — do
G_LINE.getExtents()trả về hình chữ nhật sentinel (-1000000,−1000000/2000000×2000000), lấy trung tâm cho ra pixel(0,0) → tọa độ JSXGraph không đổi. - Starburst pattern — sửa lần đầu dùng foot-of-perpendicular từ viewport center, nhưng mọi đường thẳng đều hội tụ về tâm.
- Thiếu
curveType: 'plot'→ JSXGraph bỏ qua curve khi parents là mảng số. nullseparator trong mảng → JavaScriptnullkhông phảiNaN→ JSXGraph không nhấc bút.
Khi par[0] là G_CONSTRAINED_POINT (glider) và par[1] là G_POINT (điểm truy vết):
- Export
{ "type": "tracecurve", "parents": [gliderId, traceId], "attrs": { "numberPoints": 300 } }. - JSXGraph built-in
tracecurvedi chuyển glider dọc theo slideObject, cập nhật toàn bộ chuỗi phụ thuộc, ghi lại tọa độ của điểm truy vết → hoàn toàn động, cập nhật khi kéo bất kỳ đối tượng cha nào.
Nghiên cứu GSP/WSP — cách GSP giải quyết:
- File GSP JSON:
{ "kind": "Sampler", "genus": "Envelope" }vớidrivenObject(đường thẳng d),driverObject(điểm C trên đường tròn),domainObject(đường tròn),numSamples: 58. - Web Sketchpad (
wsp.js) —GSP.gConstraints.GeomLocus(line 56642) kế thừaLocus→DriveChain:DriveChain.updateConstraintAncestry(): clone toàn bộ chuỗi phụ thuộc từ driverObject đến drivenObject bằngsketch.cloneGObjects(toClone).GeomLocus.prepareDriverWalkInfo(): tạo mảngnumSamplesvị trí đều nhau trêndomainObject(đường tròn) bằngdrivePath.mapVectorPathValuesToPositions(pathValues).Locus.constrain(): loopnumSampleslần — đặtclonedDriver.geom.loc = positions[i]→sketch.constrainDriveChain(driveChain)→ cloneddrivenObject(Straight/Line) tự tạovectorContextvà ghi geometry của mình bằngfillSingleSample().- Cloned driven object có thể là bất kỳ loại geometry nào (Point, Straight, Circle…) — mỗi loại implement
createVectorContext()vàfillSingleSample()riêng.
- Điểm mấu chốt: WSP clone toàn bộ chain để không làm bẩn board thật; sau đó sample độc lập trên chain clone.
Giải pháp trong HeyGeo Web — mirror thuật toán WSP trực tiếp trong JSXGraph:
JSXGraph không có built-in tracecurve cho line-object (chỉ hỗ trợ point). Ta tạo custom element objectlocus:
- C++ exporter (
HeyGeoWebExporter.cpp): khipar[1]làG_LINE, export:{ "type": "objectlocus", "parents": ["gliderId", "drivenLineId"], "attrs": { "numberPoints": 150 } } - JSXGraphViewer.tsx: handler
objectlocustạo JSXGraphcurvevới customupdateDataArray:- Loop
numberPointslần quaglider.slideObject.minX()→maxX()(giốngtracecurvenội bộ của JSXGraph, xemjsxgraph/src/base/curve.jsline 2610). glider.setPositionDirectly(COORDS_BY_USER, [x, y])tại mỗi bước.- Cập nhật chuỗi phụ thuộc từ glider → drivenLine theo thứ tự insertion (duyệt
board.objects) — không cần clone vì JSXGraph update là synchronous và ta restore sau loop. - Đọc
drivenLine.stdform([c0, c1, c2]vớic0 + c1·x + c2·y = 0) → hướng đường(-c2, c1), base point =drivenLine.point1(luôn là điểm finite ngay cả khipoint2là ideal point ở vô cực). - Extend ±15 board-units để tạo đoạn thẳng hữu hạn, nối bằng
NaNseparator. - Restore
glider.positionvà re-constrain chain.
- Loop
Tại sao dùng stdform thay vì point1/point2 của line?
- JSXGraph
normal(perpendicular line) cópoint2là ideal point với tọa độInfinity— không thể dùng để compute endpoint. stdform[1] = c1,stdform[2] = c2luôn hữu hạn và cho direction vector(-c2, c1)chính xác.
src/app/HeyGeoWebExporter.cpp— G_LOCUS case: thêm nhánhtracecurve(G_POINT) vàobjectlocus(G_LINE) trước static snapshot fallback.HeyGeoWeb/components/JSXGraphViewer.tsx:- Thêm handler
objectlocusvớiupdateDataArraycustom mirror thuật toán WSP GeomLocus. - Convert
null → NaNtrong parents mapping cho curve plot separator.
- Thêm handler
- Luôn đọc nguồn gốc (
wsp.js) trước khi implement — thuật toán DriveChain/GeomLocus của WSP rất chuẩn và có thể mirror trực tiếp vào JSXGraph mà không cần clone chain. stdformlà API bền vững nhất của JSXGraph Line — không bị ảnh hưởng bởi kiểu line (two-point, parallel, normal/perpendicular).tracecurvenội bộ JSXGraph (curve.js line ~2610) là template tuyệt vời: loop glider position →setPositionDirectly→ update objects in insertion order → record → restore.- Không dùng
getExtents()củaG_LINEcho bất kỳ mục đích tọa độ nào — trả về sentinel rect vô hạn. - JSXGraph
objectlocuslà fully dynamic: kéo bất kỳ điểm cha nào (D, A, B, đường tròn) đều tính lại bao hình tức thì.
- Format nhãn đo đạc (lhs) — bỏ tiền tố dài, bắt chước GSP:
- Distance:
Distance A B→AB(tên hai điểm ghép liền) - Length:
Length s_(1)→s_(1)(chỉ tên đối tượng) - Angle:
Angle BAC→m∠BAC(ký hiệu m∠ chuẩn) - Ratio:
Ratio AB/CD→AB/CD(không có tiền tố)
- Distance:
- Giá trị có đơn vị vật lý — tính từ DPI vật lý màn hình (
QScreen::physicalDotsPerInchX()/devicePixelRatio()):- Distance / Length / Radius: hiển thị cm, 2 chữ số thập phân (VD:
AB = 2.36 cm) - Angle: hiển thị ° (ký hiệu Unicode U+00B0), 2 chữ số thập phân
- Area: hiển thị cm² (U+00B2)
- Ratio / Slope / tọa độ: dimensionless, 2 chữ số thập phân
- Distance / Length / Radius: hiển thị cm, 2 chữ số thập phân (VD:
G_valueObject::displayText— thêm trườngQString displayTextlưu chuỗi plain-text đầy đủ (VD:"AB = 2.36 cm") dùng cho JSXGraph export; gettergetDisplayText()vàgetPos()được thêm vào public API.- JSXGraph export G_MEASURE —
HeyGeoWebExporternay dùngvalObj->getPos()(vị trí thực của text object) vàvalObj->getDisplayText()(chuỗi GSP-style) thay vìref->getLabel().getText()(luôn là"M1"/"M2"...). G_pointObject::update()drag fix —BuildReversevàHeyGeoViewmouseMoveEvent được cập nhật để nhận diệnG_HORIZONTAL_POINTnhư một loại điểm ràng buộc có thể kéo (tương tựG_CONSTRAINED_POINT), cho phép điểm U trên hệ trục tọa độ có thể kéo để co dãn đơn vị.
src/geometry/G_measureObject.cpp—updatelhs(): đổi format nhãn;G_measureObject::update(): thêm đơn vị cm/°/cm²,displayText;G_valueObject::update(): bỏ 4 chữ số về 2 chữ số; thêm#include <QScreen>src/geometry/G_object.H— thêmdisplayText,getDisplayText(),getPos()vàoG_valueObjectsrc/app/HeyGeoWebExporter.cpp— G_MEASURE/G_CALCULATE case: dùnggetPos()+getDisplayText()src/app/BuildReverse.cpp— checkG_HORIZONTAL_POINTcho single-constrained-point shortcutsrc/app/HeyGeoView.cpp— mouseMoveEvent: thêmG_HORIZONTAL_POINTvào điều kiện drag
G_HORIZONTAL_POINT— kiểu điểm ràng buộc ngang mới trong enumG_PointType(defs.H):- Luôn bám theo cha (điểm O) theo phương ngang:
y = parent.y - Khoảng cách
dx = x − parent.xluôn ≥ 10 px (không cho vượt qua O) - Serialization: lưu/đọc
p(khoảng cách dx) giốngG_CONSTRAINED_POINT
- Luôn bám theo cha (điểm O) theo phương ngang:
- Điểm O (Origin) — điểm tự do biểu diễn gốc hệ tọa độ; kéo O kéo toàn bộ hệ trục theo.
- Điểm U (Unit) — điểm
G_HORIZONTAL_POINTcon của O; kéo U co dãn đơn vị trục X; nhãn ẩn (setLabelVisible(false)). - Bỏ nhãn "O" cứng trong
G_coordsys::draw()— không còn vẽ chuỗi "O" tại gốc; điểm O tự hiển thị nhãn như mọi điểm khác.
src/defs.H— thêmG_HORIZONTAL_POINTvàoenum G_PointTypesrc/geometry/G_pointObject.cpp—update(): caseG_HORIZONTAL_POINT;save()/load(): serialize p;resetP()src/geometry/G_object.H—resetP(): thêm điều kiệnG_HORIZONTAL_POINTsrc/geometry/G_coordsys.cpp—draw(): bỏp.drawText(..., "O")src/app/menu.cpp—graphNewCoordSys(): tạo điểm U vớiG_HORIZONTAL_POINT, O là parent, ẩn nhãn U
Measure > Coordinates(G_COORDINATES_MEASURE) — hiển thịP = (x; y)trong hệ tọa độ hiện tại.Measure > Abscissa x(G_ABSCISSA_MEASURE) — hiển thịx_P = val.Measure > Ordinate y(G_ORDINATE_MEASURE) — hiển thịy_P = val.Measure > Equation(G_EQUATION_MEASURE) — hiển thị phương trình đường thẳng/đoạn/tia dạngAx + By + C = 0, hoặc phương trình đường tròn dạng(x-h)²+(y-k)²=r².- Bốn loại đo trên yêu cầu có
G_COORDSYStrong document; giá trị được tính quaG_coordsys::screenToMath().
src/defs.H— thêm 4 enum vàoG_MeasureTypesrc/geometry/G_measureObject.cpp—calculate()+updatelhs()+update(): xử lý 4 loại mớisrc/app/cans.cpp— thêmcanMeasureCoordinates(),canMeasureAbscissa(),canMeasureOrdinate(),canMeasureEquation()src/app/menu.cpp— thêmmeasureCoordinates(),measureAbscissa(),measureOrdinate(),measureEquation()src/app/HeyGeoDocument.H— khai báo các can + slot mớisrc/app/HeyGeoMenus.cpp— nối dây 4 menu item mới vào Measure menu
Graph > New Coordinate System— tạo hệ trục tọa độ (G_COORDSYS) với gốc O và điểm đơn vị U; hỗ trợ kéo O (di chuyển hệ trục) và kéo U (đổi tỷ lệ đơn vị).Graph > Define Axes...— dialog cấu hình nhãn trục X/Y choG_COORDSYS.Graph > New Function...— dialog nhập biểu thứcf(x), tạo đối tượngG_FUNCTION(ẩn, làm cha cho đồ thị).Graph > Plot Function— vẽ đồ thịy = f(x)trên hệ trục tọa độ (G_FUNCTIONPLOT).Graph > Plot Curve— vẽ đường tham số(x(t), y(t))(G_PARAMCURVE).Graph > Plot As (x, y)— đặt một điểm lên hệ tọa độ theo giá trị đo (x, y).Graph > Plot Value on Axis— đặt điểm lên trục X theo một giá trị đo.Graph > Grid Form— toggle hiển thị lưới tọa độ.Graph > Snap Points— toggle snapping điểm vào lưới.- Các kiểu đối tượng mới:
G_COORDSYS,G_FUNCTION,G_FUNCTIONPLOT,G_PARAMCURVEtrongenum G_refType(defs.H). - JSXGraph export: tất cả kiểu trên đều được xuất sang JSXGraph JSON (
HeyGeoWebExporter).
src/defs.H— thêmG_COORDSYS,G_FUNCTION,G_FUNCTIONPLOT,G_PARAMCURVE; cập nhậtG_GEOMETRIC,G_VALUEsrc/geometry/G_coordsys.H/.cpp—G_coordsys,G_coordsysObject(origin, scale, grid, snap, math↔screen)src/geometry/G_function.H/.cpp—G_functionDef,G_functionObject,G_functionPlotObject,G_paramCurveObject;G_functionDef::toJavaScript()src/geometry/G_graphObjects.cpp— draw, update, save/load cho FUNCTIONPLOT và PARAMCURVEsrc/app/HeyGeoGraphDialogs.H/.cpp—HeyGeoCoordSysDialog,HeyGeoNewFunctionDialog,HeyGeoPlotFunctionDialog,HeyGeoPlotCurveDialogsrc/app/menu.cpp— implement tất cả Graph handlerssrc/app/cans.cpp— implement tất cảcanGraph*()predicatessrc/app/HeyGeoDocument.H— khai báo Graph slots và can-functionssrc/app/HeyGeoMenus.cpp—HeyGeoGraphMenuMaintainernối dây toàn bộ Graph menusrc/geometry/G_ref.cpp—createObject(): xử lý 4 kiểu mớisrc/app/HeyGeoWebExporter.cpp— xuất G_COORDSYS (axes + ticks), G_FUNCTION (skip), G_FUNCTIONPLOT (functiongraph), G_PARAMCURVE (curve)heygeo.pro— thêm các file nguồn mới
-
G_drawstyle::match()— mất pen & brush khi tạo drawstyle mới (src/geometry/G_drawstyle.cpp)
Một lỗi do chỉnh sửa trước đây đã vô tình xoá hai dòng gánpenvàbrushcho drawstyle mới tạo. Hậu quả: mọi yêu cầu đổi màu/kiểu nét đều bị bỏ qua vì drawstyle mới luôn dùng màu đen mặc định.
→ Khôi phục lại:if(p != 0) newDrawstyle->pen = *p; else newDrawstyle->pen = QPen(); if(b != 0) newDrawstyle->brush = *b; else newDrawstyle->brush = QBrush(Qt::black, Qt::Dense4Pattern);
-
canEditChangeLinestyle()— quá nghiêm ngặt (src/app/cans.cpp)
Trước đây yêu cầu tất cả đối tượng được chọn đều phải làG_CURVE | G_LOCUS. Nếu chọn hỗn hợp (đường + điểm) thì toàn bộ menu Line Style bị vô hiệu.
→ Đổi sang: cho phép nếu ít nhất một đối tượng được chọn làG_CURVE | G_LOCUS. -
getCurrentPenstyle()/getCurrentPenWidth()(src/app/HeyGeoDocument.cpp)
BỏQ_ASSERT(canEditChangeLinestyle())và sửa để chỉ duyệt qua các ref kiểuG_CURVE | G_LOCUSkhi đọc style hiện tại (bỏ qua điểm trong selection hỗn hợp). -
setCurrentPenstyle()/setCurrentPenWidth()(src/app/HeyGeoDocument.cpp)
Thêmif(!(... & (G_CURVE | G_LOCUS))) continue;trong vòng lặp — chỉ áp dụng kiểu nét/độ dày cho đường cong và quỹ tích, bỏ qua điểm trong selection hỗn hợp.
- Menu Display → Color — mỗi mục hiển thị ô màu 16×16 kèm tên màu; có dấu ✓ theo màu hiện tại.
- Menu Display → Line Style — mỗi mục hiển thị icon nét (Solid/Dashed/Dotted) hoặc độ dày (Thin/Normal/Thick) cùng nhãn; có dấu ✓.
- Menu Display → Point Style — mỗi mục hiển thị icon điểm (Large/Medium/Small) cùng nhãn; có dấu ✓.
- Menu Display → Font — thêm danh sách font family (Latin Modern Roman, CMU Serif, Times New Roman, Arial, Courier New) phía trên danh sách cỡ chữ; mục "Font..." mở dialog chọn font hệ thống.
- Font mặc định đổi từ
"Arial"sang"Latin Modern Roman"(G_drawstyle::match()vàG_drawstyle::defaultStyle()). - KFormula font sync —
G_label::draw(),setString(),setText()gọioutput.setFont(d.getFont())để đồng bộ font renderer KFormula với drawstyle. - Web Exporter font —
HeyGeoWebExporter::refToAttrs()xuấtlabel.fontFamilyvàlabel.fontSizedưới dạng JSXGraph attrs; ánh xạ"Latin Modern Roman"→'Latin Modern Math', 'STIX Two Math', serif.
src/geometry/G_drawstyle.cpp— sửamatch(): khôi phục gán pen/brush; đổi font mặc định sang Latin Modern Romansrc/app/cans.cpp— sửacanEditChangeLinestyle(): any-match thay vì all-matchsrc/app/HeyGeoDocument.cpp— sửagetCurrentPenstyle/Width(),setCurrentPenstyle/Width(): hỗ trợ selection hỗn hợpsrc/app/HeyGeoMenus.cpp—ColorInfo,LinestyleInfo,PointstyleInfo: icon + tên + checkable;FontFamilyInfomới;HeyGeoEditFontMenuMaintainerthêm font family listsrc/geometry/G_label.cpp— sync font KFormula trongdraw(),setString(),setText()src/app/HeyGeoWebExporter.cpp— xuất font info (family, size) vào JSON
HeyGeoWebExporter::generateSvgThumbnail()— render canvas hiện tại ra chuỗi SVG in-memory (sử dụngQSvgGenerator+QPainter, kích thước 400×300 px) trước khi upload.uploadToServer()nhận thêm tham sốthumbnailSvgvà gửi kèm trong JSON payload dưới trườngthumbnailSvg.HeyGeoWindow— gọigenerateSvgThumbnail()ngay trước bước upload; truyền SVG string vàouploadToServer().- Prisma schema — thêm field
thumbnailSvg String? @db.Textvào modelModel. - API POST
/api/models— nhận và lưuthumbnailSvgvào DB. - API PUT
/api/models/[id]— cho phép cập nhậtthumbnailSvg. - API GET
/api/models— trả vềthumbnailSvgcùng với dữ liệu model. ModelCard.tsx— hiển thị SVG thumbnail bằng<img src="data:image/svg+xml;...">khi cóthumbnailSvg; ngược lại vẫn hiển thị placeholder như cũ.dashboard/page.tsx— truyềnthumbnailSvgxuốngModelCard.
Trước đây thumbnail trên heygeo.pedu.vn được tạo bằng cách render lại JSON qua JSXGraph mini — cấu trúc toán học thường hiển thị không chính xác. Nay thumbnail là SVG được render trực tiếp từ canvas HeyGeo Desktop lúc export, đảm bảo hiển thị đúng 100%.
src/app/HeyGeoWebExporter.H— khai báogenerateSvgThumbnail(), cập nhật signatureuploadToServer()src/app/HeyGeoWebExporter.cpp— implementgenerateSvgThumbnail(), cập nhậtuploadToServer()src/app/HeyGeoWindow.cpp— gọigenerateSvgThumbnail()và truyền vàouploadToServer()HeyGeoWeb/prisma/schema.prisma— thêm fieldthumbnailSvgHeyGeoWeb/app/api/models/route.ts— GET + POST nhận/trảthumbnailSvgHeyGeoWeb/app/api/models/[id]/route.ts— PUT cập nhậtthumbnailSvgHeyGeoWeb/app/(main)/dashboard/page.tsx— truyềnthumbnailSvgvàoModelCardHeyGeoWeb/components/ModelCard.tsx— render SVG thumbnail
- Đo lường hệ toạ độ (Desktop) — bốn loại đo mới liên kết với
G_COORDSYS:Measure > Abscissa x— hoành độx_Pcủa điểm P trong hệ toạ độ.Measure > Ordinate y— tung độy_Pcủa điểm P trong hệ toạ độ.Measure > Coordinates— cặp(x_P; y_P).Measure > Coordinate Distance— khoảng cáchCDtính theo đơn vị hệ toạ độ.
- G_COORDSYS Export to Web —
HeyGeoWebExporterxuất hệ trục thành các phần tử JSXGraph:- Hai line (kiểu
"line") thay cho"axis"để đảm bảoneedsRegularUpdate=truevà không sinh lưới giả. - Hai ticks với
insertTicks:false,ticksdistancekhớp đơn vị HeyGeo tại thời điểm xuất. - Điểm V ẩn (hàm vị trí) = U quay 90° CCW quanh O — định hướng trục Oy.
- Ba eventbinding phụ trách hành vi tương tác (xem mục Changed).
- Hai line (kiểu
- Hiển thị subscript — giá trị đo dạng
x<sub>C</sub>(HTML subscript) trong JSXGraphViewer. axis ?? falsedefault trongJSXGraphViewer.tsx— ngăn JSXGraph tự sinh lưới mặc định khi JSON không khai báo trườngaxis.
G_HORIZONTAL_POINT→ draggable —HeyGeoWebExporterkhông còn xuất điểm U(1;0) dưới dạngfixed:true; nay xuất cùng nhánh vớiG_FREE_POINT.- Hệ trục rigid khi kéo O (
eventbinding: move_with, parents=[O, U]):
Khi người dùng kéo O, điểm U di chuyển theo cùng delta → hệ trục bình tịnh. - U bị ràng buộc ngang + thay đổi tỷ lệ (
eventbinding: coordsys_u_drag, parents=[U, O, xTicks, yTicks]):- U chỉ di chuyển theo phương ngang, luôn nằm bên phải O (tối thiểu 0.1 đơn vị).
- Sau mỗi lần kéo U,
xTicks.setAttribute({ticksdistance: dist})vàyTicks.setAttribute(...)cập nhật khoảng chia ngay lập tức → tỷ lệ hệ trục thay đổi trực quan.
- Measure funcStrings — tính
sf = |OU|động tại runtime từ toạ độ sống của O và U trongboard.objects; giá trị đo được cập nhật khi kéo U. JSXGraphViewer.tsx— bổ sung xử lý hai loạieventbindingmới (move_with,coordsys_u_drag) trong vòng lặp khởi tạo phần tử.
- Bỏ lưới toạ độ thừa — thay
"axis"bằng"line"(axis.needsRegularUpdate=falsegây đóng băng trục;axis.ticks.majorHeight=-1tạo ra các vạch chạy ngang toàn màn hình giống lưới). - Trục theo O khi kéo — trước đây O và U là hai điểm độc lập; nay binding
move_withgiữ khoảng cách O→U không đổi.
src/app/HeyGeoWebExporter.cpp— G_POINT case (G_HORIZONTAL_POINT), G_COORDSYS case (toàn bộ), measure funcStringsHeyGeoWeb/components/JSXGraphViewer.tsx—axis ?? false, xử lýeventbindingmove_with & coordsys_u_drag
- GSP-style Transform Dialogs (
src/app/HeyGeoTransformDialog.H/.cpp):HeyGeoTranslateDialog— 3 tab: By Marked Vector / Rectangular (dx, dy) / Polar (distance, angle).HeyGeoRotateDialog— 2 tab: By Marked Angle / Fixed Angle (degrees + CCW/CW radio).HeyGeoDilateDialog— 2 tab: By Marked Ratio / Fixed Ratio (scale factor).
- Mark Distance (
Transform > Mark &Distance) — kết nốitransformChooseDistance()vàcanTransformChooseDistance()(chọn 2 điểm hoặc 1 đoạn thẳng); hiển thị đường highlight màu vàng + tick marks ở mỗi đầu trongdraw(). createConstantValueRef(double)— tạo G_CALCULATE ref vô hình với giá trị hằng số, dùng cho Rotate/Dilate Fixed mode.createFixedPointRef(double, double)— tạo G_FREE_POINT ref vô hình tại toạ độ tài liệu chỉ định, dùng cho Translate Fixed mode.
Transform > Translate...— thay silent action bằng dialog; tab "By Marked Vector" sử dụng t_vector như cũ; tab "Rectangular/Polar" tạo hai điểm helper vô hình rồi thực hiện G_TRANSLATED.Transform > Rotate...— thay silent action bằng dialog; tab "By Marked Angle" giữ hành vi cũ; tab "Fixed Angle" tạo G_CALCULATE ref với giá trị góc và thực hiện G_ROTATED với 3 cha.Transform > Dilate...— thay silent action bằng dialog; tab "By Marked Ratio" giữ hành vi cũ; tab "Fixed Ratio" tạo G_CALCULATE ref với giá trị tỷ lệ.canTransformTranslate()— bỏ yêu cầut_vector.count() > 0; chỉ cần có đối tượng được chọn (dialog cung cấp chế độ Fixed).canTransformRotate()— bỏ yêu cầut_angle.count() > 0; vẫn cần t_center.canTransformScale()— bỏ yêu cầut_ratio.count() > 0; vẫn cần t_center.canTransformClearChosen()— kể thêmt_distance.transformClearChosen()— xoá thêmt_distance.delRef()— thêmif(t_distance.containsRef(inRef)) t_distance.clear().HeyGeoDocument.H— thêmt_distance,canTransformChooseDistance(),transformChooseDistance(),createConstantValueRef(),createFixedPointRef()vào lớp HeyGeoDocument.
fixedAngleDeg()trongHeyGeoRotateDialog— sửa công thức: CCW trả về+θ, CW trả về360 − θ(khớp với(360 − value)/180·πtrongupdateTransform()).
HeyGeoWebExporter::appendElements()— sửa các bug gây xuất JSON sai cho đối tượng được transform:G_SEGMENTIS_TRANSFORM: thêm guard!IS_TRANSFORM(dtype)trước branchpar.size() >= 2.G_CIRCLEIS_TRANSFORM: tương tự guard; thêm fallback dynamic translate.G_CIRCLEINTERIORIS_TRANSFORM: guard + fallback (trước đây break ngay, không có output).G_POLYGONIS_TRANSFORM: guard + dynamic translate từng đỉnh thông qua JSXGraph transform element.G_POINT + G_TRANSLATED: xuất JSXGraph{type:"transform", attrs:{type:"translate"}}+ point tham chiếu source + transform (dynamic).G_POINT + G_REFLECTED: xuất JSXGraph{type:"transform", attrs:{type:"reflect"}}với đường gương.
- Point on Object (
Construct > Point on Object) — tạo điểm bị ràng buộc (G_CONSTRAINED_POINT) trên đường thẳng, đoạn thẳng, tia, đường tròn hoặc cung tròn được chọn; điểm được đặt tại tham số t = 0.5 (giữa đường). - Arc on Circle (
Construct > Arc on Circle) — thêm kiểu cung mớiG_ONCIRCLE_ARC(cha: 1 đường tròn + 2 điểm); cung được tính toán bằng cách chiếu hai điểm lên đường tròn, sau đó nội suy CCW. canNewPointOnObject()— kích hoạt menu khi ≥ 1 đối tượng đường (straight | circle | arc) được chọn.canNewArcOnCircle()— kích hoạt menu khi chọn đúng 1 đường tròn + 2 điểm.canNewCircleCenterPoint()— kích hoạt menu khi chọn đúng 2 điểm.canNewCircleCenterRadius()— kích hoạt menu khi chọn 1 điểm + 1 đoạn thẳng.
Construct > Circle by Center+Point— thayNewCInfo(stub) bằngPCInfokết nối vàonewCircle()với điều kiệncanNewCircleCenterPoint().Construct > Circle by Center+Radius— thayUDInfobằngPCInfokết nối vàonewCircle()với điều kiệncanNewCircleCenterRadius().Construct > Arc on Circle— thayUDInfobằngPCInfokết nối vàonewArcOnCircle()mới.Construct > Point on Object— thayUDInfobằngPCInfokết nối vàonewPointOnObject()mới.G_arcObject::update()— bổ sung xử lý caseG_ONCIRCLE_ARC: tính góc của hai điểm trên đường tròn rồi dựngG_arctheo chiều CCW.
- Angle Bisector (
G_BISECTOR_RAY) — trước đây rơi vào fallback generic ray; nay xuất đúng kiểu JSXGraph"anglebisector"với parents[p1, vertex, p2]. - Arc qua 3 điểm (
G_THREEPOINTS_ARC) — trước đây xuất sai kiểu"arc"(cần center+start+end); nay xuất đúng"circumcirclearc"(3 điểm trên cung). - Arc on Circle (
G_ONCIRCLE_ARC) — xuất kiểu JSXGraph"arc"với parents là[center_point, startPoint, endPoint]; center lấy từ parents của đường tròn cha. - Center Point (
G_CENTER_POINT) — cải thiện: ưu tiên tham chiếu đến điểm tâm của đường tròn cha thay vì tạo một điểm cố định.
src/defs.H— thêmG_ONCIRCLE_ARCvào enumG_ArcTypesrc/geometry/G_arcObject.cpp— thêm caseG_ONCIRCLE_ARCtrongupdate(); thêm#include <cmath>src/app/cans.cpp— thêmcanNewPointOnObject(),canNewArcOnCircle(),canNewCircleCenterPoint(),canNewCircleCenterRadius()src/app/menu.cpp— thêmnewPointOnObject(),newArcOnCircle()src/app/HeyGeoDocument.H— thêm khai báo các can-function và slot mớisrc/app/HeyGeoMenus.cpp— nối dây lại 4 menu item trong Construct; xóacircleFunckhông dùngsrc/app/HeyGeoWebExporter.cpp— sửa 4 lỗi xuất JSON (bisector, 3-point arc, arc-on-circle, center point)
-
Phép tịnh tiến không cập nhật khi kéo vector (Desktop → Web): Khi thay đổi vector tịnh tiến (kéo điểm đầu/cuối), ảnh của các đối tượng trên web viewer không thay đổi theo — vì transform được xuất với giá trị số cố định tại thời điểm export.
-
TypeError: Cannot read properties of undefined (reading 'X'): JSXGraph render error khi mở mô hình có phép tịnh tiến. Hai nguyên nhân gốc:
- JessieCode compiled mode:
getvarJS()không resolve bare identifier thành board object (code path đó bị comment out trong JSXGraph). Biểu thứcX(p0)biên dịch thành$jc$.X(p0)trong đóp0là biến JSundefined→ crash. - JSXGraphViewer.tsx không truyền
idvàoboard.create()→ JSXGraph tự sinh ID ngẫu nhiên →board.objects['p0']=undefineddù có dùng$('p0')notation.
- JessieCode compiled mode:
-
HeyGeoWebExporter.cpp— 5 G_TRANSLATED sites (G_POINT, G_SEGMENT, G_CIRCLE, G_CIRCLEINTERIOR, G_POLYGON): đổi JessieCode expression từ bare identifier sang$('id')notation:- Trước:
"X(%1)-X(%2)"/"Y(%1)-Y(%2)" - Sau:
"X($('%1'))-X($('%2'))"/"Y($('%1'))-Y($('%2'))" $('id')được JessieCode biên dịch thành$jc$.board.objects['id']— runtime board ID lookup chính xác.
- Trước:
-
HeyGeoWeb/components/JSXGraphViewer.tsx— truyềnidvàoattrskhi gọiboard.create():// Trước: const created = board.create(el.type, parents, el.attrs ?? {}); // Sau: const attrs = el.id ? { id: el.id, ...(el.attrs ?? {}) } : (el.attrs ?? {}); const created = board.create(el.type, parents, attrs);
Đảm bảo JSXGraph đăng ký element trong
board.objectsvới đúng ID (p0,p1,seg0...) để JessieCode$('id')có thể tìm thấy tại runtime.
rsync.md— tài liệu hoá quy trình build & deploy từ local (iMac) lên server (M2 Pro / heygeo.pedu.vn):- Lệnh pull/push qua rsync
- Build Next.js standalone trên server (
/opt/homebrew/bin/npm run build) - Copy static files vào
.next/standalone/và restart LaunchDaemoncom.heygeo.app - Dùng
sshpass -f ~/.heygeo_pass(password lưu file chmod 600, không commit)
src/app/HeyGeoWebExporter.cpp— 5 G_TRANSLATED sitesHeyGeoWeb/components/JSXGraphViewer.tsx— truyềnidvàoboard.create()rsync.md— quy trình build & deploy
- Không thể mở file
.seg/.sectrong thư mụcexamples/: Các file này được tạo bởi ứng dụng gốc KSEG nên chứa version string"KSeg Document Version 0.2"/"KSeg Construction Version 0.2"trong phần header nhị phân. Sau khi đổi tên thương hiệu sang HeyGeo, hàmparseVersionStringchỉ nhận dạng tiền tố"HeyGeo ..."→ toàn bộ file KSEG cũ bị từ chối với thông báo "Invalid file".
HeyGeoDocument::parseVersionString()(src/app/HeyGeoDocument.cpp) — bổ sung nhận dạng tiền tố"KSeg Document Version "và"KSeg Construction Version "song song với"HeyGeo ...". Không cần chuyển đổi file cũ — ứng dụng tự nhận ra cả hai định dạng khi mở.
- Xóa menu Play khỏi menu bar — construction playback không còn trong menu chính (chức năng vẫn tồn tại trong code nhưng không được hiển thị).
- Đổi tên menu "View" → "Windows" (
HeyGeoViewMenuMaintainer→HeyGeoWindowsMenuMaintainer) để khớp với GSP. Cấu trúc lại theo chuẩn GSP Windows menu:- New View (chức năng hiện có)
- Phần điều hướng: Pan, Zoom In, Zoom To Fit, Original Zoom (toolbar items)
- Minimize / Zoom (stub — Mac OS X window management)
- Bring All to Front (stub — Mac OS X)
- Thứ tự menu chính giờ là:
File → Edit → Display → Construct → Measure → Number → Transform → Graph → Windows → [Construction] → Help - Phím tắt chuẩn GSP — cập nhật theo
GSP-reference/Reference Center/menus_*.htm:- Edit > Redo:
Ctrl+Y→Ctrl+R(đúng theo GSP) - Display > Show/Hide Label:
Ctrl+L→Ctrl+K(đúng theo GSPmenus_display_showhidelabel.htm) - Display > Show All Hidden: đã xóa shortcut
Ctrl+U(GSP không có shortcut cho mục này) - Construct > Midpoint:
Alt+M→Ctrl+M(đúng theo GSP) - Construct > Intersection:
Alt+I→Ctrl+I(đúng theo GSP) - Construct > Segment:
Alt+S→Ctrl+L(đúng theo GSP)
- Edit > Redo:
- Thứ tự menu giờ giống GSP 5:
File → Edit → Display → Construct → Measure → Number → Transform → Graph → View → Play → Help - File menu — sắp xếp lại theo GSP: New Sketch / New Construction, Open, Close, Save / Save As, Document Options (stub), Print Preview (stub), Print, Export to Image / SVG, Language, Quit; recent files list vẫn giữ nguyên.
- Edit menu — theo chuẩn GSP: Undo/Redo, Cut/Copy/Paste (stub), Clear (= Delete), Select All / Select Parents / Select Children (stub), Split/Merge (stub), Edit Definition (stub), Properties (stub), Preferences, Action Buttons (stub), Selection Groups.
- Construct menu (đổi tên từ "New") — thứ tự theo GSP: Point on Object (stub), Midpoint, Intersection, Endpoints, Center Point; Segment, Ray, Line, Perpendicular Line, Angle Bisector; Circle by Center+Point, Circle by Center+Radius (stub), Arc on Circle (stub), Arc through 3 Points; Polygon Interior, Circle Interior, Arc Sector Interior, Arc Segment Interior; Locus.
- Measure menu — bổ sung theo GSP: Perimeter (stub), Circumference (stub), Arc Angle (stub), Value of Point (stub), Coordinates / Abscissa / Ordinate (stub), Coordinate Distance (stub), Equation (stub).
- Transform menu — đổi tên "Choose →" thành "Mark →" cho đúng thuật ngữ GSP; đổi "Scale" thành "Dilate"; thêm Iterate (stub), Define/Edit Custom Transforms (stub). Mark items đứng trước Transform operations theo chuẩn GSP.
- Display menu (mới hoàn toàn) — tách các mục hiển thị ra khỏi Edit: Color, Line Style, Point Style, Font, Text Style (stub); Show/Hide Labels, Label Objects; Hide Objects, Show All Hidden; Animate Objects / Increase Speed / Decrease Speed / Stop Animation / Motion Controller (stub); Trace Objects / Erase Traces (stub); Change Number of Samples.
- Number menu (mới hoàn toàn) — theo GSP: New Parameter (stub), Calculate, Tabulate (stub), Add/Remove Table Data (stub), Define Function from Drawing (stub).
- Graph menu (mới hoàn toàn) — theo GSP: New Coordinate System (stub), Define Axes (stub), Plot as (x,y) (stub), Plot Value on Axis (stub), New Function (stub), Plot Function (stub), Plot Curve (stub), Plot Table Data (stub), Grid Form (stub), Snap Points (stub), Derivative (stub).
showUnderDevelopment()slot trongHeyGeoWindow— mọi mục menu chưa có tính năng thực đều gọi slot này, hiển thị hộp thoại thông báo "This feature is currently under development."UDInfoclass trongHeyGeoMenus.cpp— lớp helper gọn nhẹ để tạo nhanh menu item "stub" kết nối vàoshowUnderDevelopment().
src/app/HeyGeoMenus.cpp— toàn bộ cấu trúc menusrc/app/HeyGeoWindow.H— thêm slotshowUnderDevelopment()src/app/HeyGeoWindow.cpp— thêm implementationshowUnderDevelopment()
- Crash khi chọn Point Tool: Phát hiện vòng lặp tín hiệu đệ quy vô hạn giữa
HeyGeoToolBar::syncToolMode()vàHeyGeoView::setToolMode(). HàmsyncToolMode()gọiactivateTool(), hàm này phát signaltoolModeChanged→ gọi lạisetToolMode→ phát lại signal → gọi lạisyncToolMode→ stack overflow / crash. Sửa bằng cách táchsyncToolMode()thành hàm chỉ cập nhật trạng thái checked của button, không phát tín hiệu.
- Xóa tính năng nhấn chuột phải để tạo điểm: Hành vi cũ (right-click vào vùng trống → tạo free point; right-click vào đường → tạo điểm constrained; right-click vào giao điểm 2 đường → tạo intersection point) đã bị xóa bỏ hoàn toàn. Tính năng này không trực quan và gây nhầm lẫn với chuẩn UX của GSP.
- Context Menu khi nhấn chuột phải: Thay thế hành vi right-click cũ bằng context menu theo đúng chuẩn GSP. Right-click vào một đối tượng sẽ tự động chọn đối tượng đó và hiện menu với các mục:
- Toggle Label — bật/tắt nhãn
- Edit Label... — đổi tên nhãn
- Hide — ẩn đối tượng
- Delete — xóa đối tượng
src/app/HeyGeoToolBar.cpp— sửasyncToolMode()src/app/HeyGeoView.cpp— xóa right-click point creation, thêm context menu, thêm#include <QMenu>
-
Thanh công cụ dọc GSP-style nằm ở cạnh trái cửa sổ (
Qt::LeftToolBarArea), bao gồm 9 nhóm công cụ:# Công cụ Mô tả 1 Selection Chọn và di chuyển đối tượng (mặc định) 2 Point Đặt điểm tự do / trên đường / giao điểm bằng left-click 3 Compass Vẽ đường tròn bằng 2 click: tâm → điểm trên đường tròn 4 Straight Edge Flyout: Segment / Ray / Line (2 click) 5 Polygon Flyout: Polygon / Filled Polygon / Open Polygon (multi-click, double-click để đóng) 6 Text Click đối tượng để bật/tắt nhãn 7 Pen Đánh dấu đoạn thẳng / góc (placeholder) 8 Information Click đối tượng để xem thông tin 9 Custom Chạy construction tùy chỉnh (placeholder) -
Hệ thống Tool Mode mới trong
HeyGeoView: enumToolMode(13 giá trị), routing left-click theo tool đang active, rubber-band preview bằng đường đứt nét xanh, phímEscapeđể hủy giữa chừng. -
Icons: Tất cả icon vẽ bằng
QPainter/QPainterPath— không dùng file ảnh ngoài. -
Snapping: Khi dùng Point/Compass/Segment/Ray/Line tool, click gần điểm có sẵn (<6px) sẽ snap vào điểm đó thay vì tạo điểm mới.
src/app/HeyGeoToolBar.Hsrc/app/HeyGeoToolBar.cpp
src/app/HeyGeoView.H— thêmToolModeenum, members, slots, signalssrc/app/HeyGeoView.cpp— thêm tool routing, rubber-band,getOrCreatePoint(),handleToolClick(),handleToolDoubleClick(),drawToolRubberband()src/app/HeyGeoWindow.H— thêmgspToolBarmembersrc/app/HeyGeoWindow.cpp— khởi tạo toolbar và kết nối signalsheygeo.pro— đăng ký file mới vào build
- Màn hình Retina bị mờ: Buffer canvas chỉ render ở logical pixel (ví dụ 800×600) rồi bị Qt kéo giãn lên physical pixel (1600×1200 trên màn 2×) → mờ và ráp. Sửa bằng cách nhân buffer size với
devicePixelRatio()và gọibuffer.setDevicePixelRatio(dpr). - Đường thẳng / điểm / đường tròn bị răng cưa: Tọa độ vẽ bị ép về số nguyên (hàm
drawLine(QPoint, QPoint),drawEllipse(int,int,int,int)) khiến sub-pixel anti-aliasing bị vô hiệu. Sửa bằng cách dùngQPointFvàQRectFtrên toàn bộ code vẽ hình học.
src/app/HeyGeoView.cpp— HiDPI buffer vớidevicePixelRatio()src/geometry/G_point.H— thêm phương thứctoQPointF()src/geometry/G_point.cpp—drawEllipsedùngQRectFsrc/geometry/G_segment.cpp—drawLinedùngQPointFsrc/geometry/G_circle.cpp—drawEllipsedùngQRectFsrc/geometry/G_arc.cpp—drawArcdùngQRectFsrc/geometry/G_polygon.cpp— dùngQPolygonFthayQPolygon
- Fork từ KSEG 0.403 (Ilya Baran, MIT, 1999–2006)
- Script
rebuild.sh— tự độngqmake+make+ đóng gói.apptrên macOS Apple Silicon README.mdmới bằng tiếng Anh với hướng dẫn build cho macOS và Linux
- Viết lại toàn bộ
heygeo.pro: thêmQT += core gui widgets printsupport xml svg, bậtCONFIG += c++17, xóa cú pháp Qt 3/4 lỗi thời
| API cũ | API mới |
|---|---|
Q3PtrList, Q3Dict |
QList, QHash |
QColor(r,g,b, QColor::Hsv) |
QColor::fromHsv(h,s,v) |
bitBlt(...) |
QPainter::drawPixmap(...) |
QPaintDevice::isExtDev() |
dynamic_cast<QPrinter*>(device()) |
QPixmap::setOptimization(...) |
(removed) |
QApplication::setMainWidget(...) |
(removed) |
QDesktopWidget |
QScreen |
drawWinFocusRect(...) |
QPainter::drawRect với Qt::DashLine |
SIGNAL/SLOT macros (phần lớn) |
connect(obj, &Class::signal, ...) |
- Xóa từ khóa
register(deprecated C++17) - Sửa ambiguous overload cho
qAbs,qMax,qMinvới kiểudouble - Thay
std::hash_map/std::hash_set(SGI non-standard) bằngstd::unordered_map/std::unordered_set - Chuẩn hóa tất cả include style:
<qpainter.h>→<QPainter>, v.v.
- Port
kformula.cpp/kformulaedit.cppsang Qt 6 (widget painting, font metrics, input handling)
- Đổi tên "kseg" → "HeyGeo" trong title bar, file filter, About dialog
Dự án HeyGeo được phát triển từ nền tảng KSEG — © 1999–2006 Ilya Baran (MIT). Phiên bản HeyGeo và các nâng cấp tiếp theo thuộc về nhóm phát triển HeyGeo.