Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[0.2.0-beta.5] - 2026-08-13
Mostly the things beta.4 made visible. Its own release notes are the first ones you could actually read in an installed copy, which is how the licence notice turned out to be describing the wrong licence, and the Preferences window turned out to have been cutting off the right-hand side of every row for as long as it has existed.
Changed
Modan2 now tells you which licence applies to what. The source code is MIT, and the builds published on the releases page are GPL-3.0 — both were already true, but only the first was written down, so the About box told you the more permissive of the two. The application includes Qt through PyQt5, which is offered only under the GPL-3.0 or a commercial licence, and that makes the build as a whole GPL-3.0.
Nothing you do with Modan2 becomes harder: the GPL restricts redistributing a build, not using it, including commercially. And source you copy out of the project is MIT wherever you found it.
The licence texts now travel with the program instead of being absent entirely — the Windows installer shows the GPL before installing, and every package carries
LICENSE,LICENSES/andTHIRD-PARTY-NOTICES.md, the last of which lists what is bundled and under what terms.CVA is several times faster. Estimating the classification accuracy fits the model once per specimen, and most of that work was going into computing parts of the data reduction that were then discarded. On a 222-specimen, 72-landmark dataset it drops from about seven seconds to two. The answer is unchanged; if the faster method ever fails to settle, Modan2 falls back to the slower one, says so, and gives you the same result.
Fixed
The Preferences window no longer cuts off its right-hand side. The Large radio buttons, the 3D colours and the Browse button were all beyond the edge of a window that opened too narrow to hold its own contents — and it had no horizontal scrollbar, so they could not be reached at all.
The window now sizes itself to what it contains rather than to a fixed number, on whichever platform and font you are using. The eleven marker selectors, which were the widest thing in it, are laid out over two rows, so it is smaller than before rather than larger. If a screen is ever too small for it, there is a scrollbar now.
[0.2.0-beta.4] - 2026-08-13
The beta that beta.3 could not have been. Installing beta.3 over beta.2 left beta.2 running — the installer had been skipping the program file since long before either of them — so the first thing to say about this release is that installing it will actually replace what you have.
The rest came out of using beta.3 for real: moving a library to another drive turned out to be impossible on Windows, which is the main reason anyone moves one. And two statistical results were being reported with more confidence than the data supports; those numbers will now be lower, and that is the fix.
Changed
CVA now reports a classification accuracy you can believe, and it will be lower than the one you saw before. This is the fix, not a regression.
Two things were wrong. The analysis used every landmark coordinate as a separate variable, and once those outnumber your specimens a discriminant analysis can separate any grouping perfectly — whether or not the groups really differ. On top of that, the accuracy was measured on the very specimens the model was built from, which is a test an overfitted model cannot fail. Together they produced 100% on data with nothing in it: a 222-specimen dataset of pure random numbers, split into 11 arbitrary groups, classified without a single error.
CVA now reduces the coordinates to as many dimensions as the data can support before discriminating — by a rule it now shares with MANOVA, so the two analyses in one run finally describe the same data — and measures accuracy by setting each specimen aside in turn and classifying it with a model that has not seen it. The old figure is still reported next to the new one, named resubstitution accuracy, along with how often you would be right by always guessing the largest group. An accuracy of 25% means nothing until you know whether guessing scores 10% or 50%.
Canonical variate scores change as a result, so re-running an analysis will not reproduce numbers saved by an earlier version.
MANOVA no longer uses more variables than a small dataset can support. It kept up to 20, chosen without reference to how many specimens you had, so eight specimens could be compared using twenty variables — beyond the point where the test’s assumptions hold. It now applies the same limit as CVA, which takes the specimen and group counts into account. Datasets with plenty of specimens are unaffected; small ones were the ones getting an answer they should not have trusted.
Fixed
A failed library move no longer leaves Modan2 unable to reach your data. Modan2 closes the database before moving a library, because Windows cannot move a file that is open. If the move then failed in a way that had not been anticipated, it reported the error and carried on with the database still closed, so everything you did afterwards failed until you restarted. Your data was never at risk — a move that does not finish leaves the library exactly where it was — but the program was.
Moving your library to another drive works on Windows. Choosing a folder on a different drive —
D:\Modan2when your library was onC:— failed with a message about the paths not sharing a drive, and Modan2 never got as far as offering to move anything. Moving to a larger drive is the main reason to move a library at all, so this made the feature unusable for what it was for.Installing a new version on Windows now actually replaces the old one. Upgrading appeared to work — the installer ran to the end and reported success — and then Modan2 started up still showing the previous version, because it was the previous version. The installer was skipping the program file it had come to replace.
Every build carried the same internal version number,
0.0.0.0, and Windows installers use that number to decide whether the copy already on disk needs replacing. Same number meant “already up to date”, every time.This affected every upgrade, not only the most recent one. If you have been installing new versions and wondering why nothing seemed to change, this is why. Installing this release replaces what you have regardless of what version it thinks it is, so there is nothing you need to uninstall first.
[0.2.0-beta.3] - 2026-08-12
Beta.2 gathered everything you own into one folder. This one lets you choose which folder it is, move an existing library into it, and write the whole library out to a single archive — the first backup Modan2 has made that includes your images and 3D models and can be kept on a different disk from the library it protects.
One analysis option is withdrawn: see Removed.
Added
You can choose where Modan2 keeps your data. Preferences now has a Data folder row: point Modan2 at a larger drive, or anywhere else that suits you. The database, images, 3D models, backups and logs all move together — they are one library, and splitting them would leave either half useless on its own.
Leave it alone and everything works exactly as before.
If a folder you chose is missing at startup (an external drive not plugged in, a network share that is down), Modan2 tells you which folder and asks what to do — quit, use the default, or start anyway — instead of quietly creating an empty library there, which looks the same as losing your data.
Modan2 can move your library to the new folder for you. Choosing a folder now offers to move what you already have. It copies everything across, checks that it arrived, and only then removes the originals — so if it fails, or if you stop it partway, your data is left exactly where it was. A library split across two folders is not a possible outcome. When it finishes you can keep working; no restart is needed.
Declining is still a real answer, and one with a use: Change the setting only points Modan2 at a library that is already in the new folder, such as one you copied there yourself.
You can back up your whole library to a single file. Data ▸ Back Up Library writes every dataset, object, landmark, variable, image, 3D model and saved analysis into one
.zip. Data ▸ Restore from Backup brings them back.This exists because the backups Modan2 already made were protecting the wrong thing: they covered the database but not your images and 3D models, and they sat on the same disk as the library they were backing up — so the one failure they could not survive is the likely one.
Two things it will not do to you. A backup is complete or it is not written: an interrupted run leaves no file rather than a truncated one that looks like a backup. And if a file is recorded in the database but missing from your disk, it tells you which ones instead of quietly leaving a hole in the archive.
Restoring adds; it never replaces. Datasets arrive alongside what is already in your library, and one whose name is taken is given a new one, so a restore started by mistake cannot lose you anything.
The archive is a snapshot, which makes it exactly what belongs in the sync folder that your live library should not be in.
Modan2 warns you before you put your data in a cloud-synced folder or on a network drive. Dropbox, OneDrive, Google Drive, iCloud and the like are recognised by name, as are network shares.
Both break a live database quietly, which is why they are worth a warning rather than a footnote. Modan2 writes to the database file as you work, and a sync client will upload it mid-write; open the same library from two computers and the client will not merge it, it keeps both copies and lets them drift apart with no way to tell which is right. Over a network drive the file locking the database depends on is unreliable and can corrupt it outright.
The warning explains this and can be overridden — it is your disk. The right thing to put in a sync folder is backups and exported datasets: those are snapshots, and nothing is writing to them.
Removed
Resistant Fit superimposition is no longer offered. It does not converge. On every dataset size tested it ran out its 100-iteration cap, and raising the cap moved the answer further rather than settling it — between caps of 20 and 40 the coordinates changed by more than they had between 5 and 10. It is also far too slow to use: the scale and angle estimates are pure-Python loops over every pair of landmarks, run for every shape on every iteration, which puts a 222-specimen, 72-landmark dataset in the region of hours.
The option is gone from the analysis dialog and a request for it is now refused rather than quietly answered with Procrustes, which would return a different superimposition under the name you asked for. Choose Procrustes or Bookstein. The implementation stays in the source, with its limits documented, for whoever fixes it.
Fixed
Attached files could have been read from one folder and written to another. The storage location was resolved when the program started rather than when it was used, and inconsistently: attaching, replacing, duplicating and deleting files all ignored any change to it. Nothing was lost, because the location could not actually be changed before now — but it had to be fixed before it could be.
Changed
Log files are named
Modan2_20260729.loginstead ofModan2.20260729.log. Existing log files are left alone.Preferences moved to your operating system’s settings folder —
%LOCALAPPDATA%\PaleoBytes\Modan2on Windows,~/Library/Application Support/PaleoBytes/Modan2on macOS,~/.config/PaleoBytes/Modan2on Linux. They are settings the application can recreate, so they belong beside other applications’ settings rather than in with your datasets. Your existing preferences are copied over automatically on first launch; the old file is left alone and can be deleted.This also prepares for letting you choose where your data lives: a setting cannot be stored inside the folder it points at.
[0.2.0-beta.2] - 2026-07-28
A housekeeping beta. Nothing changes about morphometrics; what changes is where Modan2 puts things on your disk. The program used to write to four different places, two of them inside Roaming AppData — a share that follows your profile between machines on a managed network, and the wrong home for a 130 MB program folder or for scratch files. Everything you own now lives in one directory.
⚠️ Upgrading from 0.2.0-beta.1 or earlier (Windows)
The installer now performs a per-user install and identifies itself by a stable ID, so it no longer recognises installations made by earlier releases. It detects them and offers to remove the old version for you — accept, unless you have a reason to keep both.
Order matters if you decline. The Start Menu shortcut and the example datasets sit at fixed paths that both installations share, so uninstalling the old version after installing the new one takes them away from the new one too.
Your datasets, images and preferences are stored outside the program folder and are not affected either way.
Changed
Modan2 installs to
%LOCALAPPDATA%\Programsinstead of%APPDATA%(Roaming). The program folder is ~130 MB and was being synchronised with the user profile on domain-joined machines.%LOCALAPPDATA%\Programsis the standard location for an application installed for one user.The installer no longer asks for administrator rights. It was requesting elevation for a per-user install; worse, consenting with a different administrator account installed Modan2 into that account’s folder, where the actual user could not see it.
Preferences moved to
~/PaleoBytes/Modan2/preferences.json, beside the database, media, logs and backups. One folder now holds everything you own, so backing up or moving an installation is a single directory copy. Preferences from the old location (~/.modan2/config.json) are copied over automatically on first launch; the old file is left untouched and can be deleted afterwards.Temporary files also moved out of Roaming AppData into
~/PaleoBytes/Modan2/.
Fixed
--configread from one file and saved to another. Preferences changed during a session were written to the default location regardless of the path given on the command line.The installer registers a publisher and an icon in Apps & features, both of which were blank.
[0.2.0-beta.1] - 2026-07-27
The 0.2 series moves to beta. Superimposition is now complete — all three methods work in 2D and 3D — and the documentation has been rebuilt against the application after a long stretch where the published site was out of date.
Added
Bookstein superimposition. Shapes are re-expressed as Bookstein shape coordinates by fixing the dataset’s baseline landmarks to a standard position (2D uses the baseline endpoints; 3D uses a 3-point baseline). Requires a baseline defined on the dataset.
Resistant Fit superimposition (RFTRA), in both 2D and 3D. It aligns using repeated medians of pairwise landmark relationships, so a few displaced landmarks do not drag the whole fit the way they can under Procrustes.
Missing landmarks are imputed for Bookstein and Resistant Fit too, using the same shape-fitting Procrustes already used.
A Quick Start page and a file format reference (TPS, NTS, Morphologika, X1Y1) in the manual, plus a glossary and a section on calibrating an object against a known distance.
Fixed
The superimposition method selector did nothing. Choosing Bookstein or Resistant Fit silently ran Procrustes. The options were disabled until the methods were implemented, and are now live.
NTS and X1Y1 files reported a landmark count of 0. The count is now computed correctly, and both parsers have test coverage.
Information and warning messages from a running analysis were dropped. They are shown in the status bar; errors still open a dialog.
A failed dataset-package import left files behind. The rollback now removes the media and directories it had created.
Two save actions could close their window on an error instead of reporting it (landmark names, preferences).
Changed
Coordinate values above 99999 are no longer clamped when a plot’s data range is computed.
Python 3.11 is no longer supported; 3.12 is the minimum.
Documentation
The documentation site had been stuck 411 commits behind — every docs build had failed since 2026-07-24 for two separate reasons. Fixed, and the site is current again.
The published manual was checked against the code, page by page. It documented a “Mark as Missing” flow that does not exist, environment variables and command-line flags the application never had, keyboard shortcuts that were never bound, and superimposition methods it does not offer. All corrected.
Installation instructions now match the actual release files — the version-stamped installer ZIP, DMG and AppImage — and say plainly that only the Windows build is well tested.
The Korean manual is complete. Every page is translated, using the application’s own Korean interface terms.
Documentation moved to
docs/manual/, so what is published is unambiguous;docs/*.mdis now repository-only developer notes.
Internal
Ruff gained PIE, RET, SIM, PERF, A and G, and cyclomatic complexity is now enforced as a ratchet. Adopting them surfaced several real defects, including a test that asserted nothing and a function that returned a dict or None depending on which branch it took.
[0.2.0-alpha.2] - 2026-07-24
A hardening release for the 0.2 alpha: several data-loss and crash fixes found by a file-I/O/security audit and by fuzzing, plus a large internal quality push (cross-platform CI, type checking, and a complexity-refactoring campaign).
Fixed
Semi-landmark curves are no longer lost on ZIP export/import. A dataset packaged to
.zipand re-imported dropped its curve scheme and every traced curve; the package format now carries them (manifest schema 1.2, older packages still import).Polygons are no longer dropped when importing a file. A Morphologika file with a
[polygons]block imported with no polygons.Missing landmarks survive ZIP round-trips correctly, stored with the app’s “Missing” marker instead of the string “None”.
NTS files now report the correct landmark count (it was always 0).
Korean (and other non-Latin) chart text renders instead of showing boxes, via a per-glyph font fallback built from the fonts installed on the machine.
Landmark file readers tolerate non-ASCII specimen names on any locale (e.g. cp949 files on a UTF-8 system and vice-versa), including a second set of readers the earlier fix had missed.
A malformed input file no longer crashes a reader — fuzzing surfaced a Morphologika crash on a bare
[; parsers now fail with a clear error.Data Exploration no longer crashes when an analysis has fewer components than the selected axis (a scatter plot with a 3rd axis on a 2-component PCA).
A requested CVA/MANOVA that fails is reported to you, instead of the analysis claiming success while silently saving nothing.
Saving an object is now atomic — a failure while attaching its image/3D model can no longer leave a half-written object.
More dialog actions surface errors instead of silently closing the window (expanded the guarded-slot coverage), with a global crash handler as a backstop.
Importing a crafted dataset package can no longer copy files from outside the package (path-traversal hardening).
Added
Export semi-landmark curves to TPS (
CURVES=), symmetric to the existing import.
Changed / Internal
Cross-platform CI (Linux/Windows/macOS) with an import smoke test; linting, formatting, type-checking (mypy), a dependency CVE scan, and a coverage floor are now enforced.
A complexity-refactoring campaign brought the worst application function from a cyclomatic complexity of 56 down to 21, with characterization tests added for the viewers; this surfaced and fixed several of the bugs above.
Added
docs/CODE_QUALITY_GUIDE.md.
[0.2.0-alpha.1] - 2026-07-23
Semi-landmark curves: trace a curve on a specimen and have it resampled into evenly-spaced semi-landmarks for analysis, with edge-snapping auto-detection. First alpha of the 0.2 series.
Added
Semi-landmark curves. Define curves for a dataset — how many semi-landmarks each carries — then trace them on each specimen. The traced curve is resampled into evenly-spaced points along its length, and analysis treats those points like ordinary landmarks. Fixed (anatomical) landmarks keep their positions and indices; the semi-landmarks follow after them. The raw trace is kept with the specimen, so you can re-trace or change the count at any time. A dataset can be analyzed with only semi-landmarks (no fixed landmarks) as well.
Snap to curve — edge auto-detection. While tracing, the curve snaps to the strongest image edge between your clicks (a “live-wire”), so a clean outline needs only a few clicks: start and end for a gentle curve, a couple of points in between for a sharp one. On by default in curve mode; uncheck Snap to curve for a plain hand trace. Enter accepts the trace, Esc cancels.
Smooth curve. Snapped traces are smoothed to remove the pixel staircase so the semi-landmarks sit on a clean curve, while the points you clicked stay put. Toggle it with the Smooth curve checkbox.
Edit a traced curve. Select a curve to adjust it — drag a point, click the line to add one, and right-click to delete a point or the whole curve (deleting a curve also works by right-clicking it in landmark mode). Snapped curves are edited by their clicked anchors and re-snap to the edge live as you drag. The selected curve is drawn thicker with square anchor handles.
Dataset-wide landmark names. Give each landmark a name/abbreviation and a description at the dataset level; the viewer shows the name instead of the index while digitizing, with the description as a tooltip.
“Show Expected” digitizing aid (2D). Once two landmarks are placed on a new specimen, the remaining positions are predicted from the dataset mean shape and shown as a guide so you know roughly where each one goes.
Import curves from TPS.
CURVES=blocks in TPS files are read in as semi-landmark curves.
Changed
The dataset dialog is organized into tabs, and gains tables for dataset-wide landmark names and the curve scheme.
The object list shows an “LM Count” column and a “Curve” column, and refreshes on Save/Next/Previous while keeping your selection.
[0.1.12] - 2026-07-21
Legend arrangement in Data Exploration, a sharper 2D viewer, and a complete Korean interface.
Added
Arrange the legend in Data Exploration. With the legend shown, a Movable checkbox lets you drag it wherever it suits the plot, and Order… opens a list you can drag entries into the order you want (with A-Z / Z-A shortcuts). Entries used to appear in whatever order specimens happened to sit in the dataset. Both the order and the position are remembered per grouping variable and restored the next time you open the analysis.
Changed
The 2D viewer scales images more smoothly, so zooming and fitting no longer show the jagged edges that made fine detail harder to read while placing landmarks.
The Korean interface is fully translated. Strings added over several releases had never been picked up, so parts of the UI stayed in English — including some that had a translation but were being skipped when the translation file was built.
Fixed
A failure during startup now says what went wrong. The error was being reported behind the splash screen, which stays on top, so all you could see was a splash frozen on the step that failed. The splash now closes first and the message points at the log file.
--dbnow opens the database you name. The option was accepted and then ignored, so the default database was always used.
[0.1.8] - 2026-07-21
Better handling of large images, much more accurate missing-landmark estimation, and fixes for memory that was never released.
Added
Oversized images are downscaled when attached. Photos whose longer side exceeds 2560 px are stored as a smaller working copy (used for landmarking), while the pristine original is archived alongside it. Digitizing and viewing large photos is faster and lighter on memory, and no image data is lost.
“Show Original” checkbox in the object dialog. When an archived original exists, you can render the 2D view from the full-resolution image for extra detail while placing landmarks. This affects display only — landmark coordinates stay in the working-copy pixel space.
The About box links to the project page, so you can open it in your browser with a click.
Changed
Missing landmarks are estimated far more accurately. Both the “show estimate” preview and the estimation used in analysis now fit the mean shape onto the landmarks a specimen actually has — matching rotation, scale and position — before filling the gaps, and analysis repeats the estimate as the alignment settles. Estimates previously landed in the wrong place whenever a specimen was photographed at an angle; in analysis they were also computed before the specimens were aligned and then never revisited, so they were off by a large margin and that error fed into the results. On test shapes where the right answer is known, the error went from 61% of specimen size to essentially zero. Analysis results change for datasets with missing landmarks; datasets without them are unaffected.
Fixed
Zooming far into a 2D image no longer risks exhausting memory. The zoom scale is capped so the rendered image can always be allocated.
Dialogs are now released when closed. Every dialog that was opened stayed in memory for the rest of the session, so memory grew steadily as you worked — noticeably with the data exploration and analysis windows, which hold large plots. They are now freed on close.
Replacing an object’s image no longer leaves the old files behind. When the new image had a different file extension, the previous image (and its archived original) stayed on disk forever.
Deleting a dataset or an object now deletes its files too. Images, their archived originals and 3D models were left on disk indefinitely — for a dataset, its entire storage folder — so deleted data kept taking up space. Files already orphaned by earlier versions are not cleaned up automatically.
[0.1.7] - 2026-07-21
A small maintenance release: one crash fix and a 3D rendering speedup.
Changed
3D landmark spheres render much faster. Spheres are now drawn from a sphere shape compiled once and reused, instead of being rebuilt triangle-by-triangle for every landmark on every frame. Rotating and dragging 3D views with many landmarks is noticeably smoother, especially in landmark-edit mode, which draws the scene twice.
Fixed
Chart no longer fails with “string index out of range” when the selected grouping variable is blank for some objects. Those objects now appear in the legend as an unlabeled group instead of breaking the plot.
[0.1.6] - 2026-07-20
Focused on making missing landmarks work end to end — from import, through editing and display, to analysis — and on the crashes found along the way.
Added
Missing landmark handling on import
Import detects the
-999morphometrics placeholder and asks whether to treat those coordinates as missing landmarks (defaults to yes)“Always treat -999 as a missing landmark” checkbox remembers the answer
Correctly handles the invert-Y option, which negates a
-999in the Y column to+999before the scan runs
Insert a missing landmark at a chosen position
“Add Missing” now inserts before the selected row instead of only appending, so a gap can be placed where it actually belongs
The button reads “Insert Missing” or “Add Missing” depending on whether a row is selected
Missing landmarks visible in the object list — the Landmarks column shows the recorded count with the missing tally beside it in red, e.g.
9 (1). The column still sorts numerically.Italic legend labels — a grouping value wrapped in asterisks renders italic in plot legends, so taxon names typeset correctly (
*Eurekia*→ Eurekia). Works for any script, including Hangul.
Changed
Landmark table cells are validated when an edit is committed. A cell accepts a number, or
MISSING(a blank cell counts as missing); anything else reverts to the stored value with an explanatory tooltip. Previously any typo silently turned the landmark into a missing one.Edits to the landmark table now update the viewer immediately rather than only at save time.
Analysis errors say what to do. A landmark-count mismatch names the object, both counts, and points at “Insert Missing”; a landmark missing in every object is named up front instead of failing deep inside PCA.
Fixed
Clearing a landmark cell no longer breaks the dataset. A blank tab/comma field produced a short landmark row, crashing
count_landmarks,has_missing_landmarksand Procrustes superimposition withIndexErrorand taking the whole dataset’s analysis with it.Clearing only the X coordinate no longer shifts Y into X’s slot — a silent coordinate corruption that produced no error at all.
Landmark-count consistency used two conflicting definitions, so an object with a missing landmark could be rejected against its own count while the Procrustes gate accepted the same dataset.
Analysis on a landmark missing in every object no longer fails with
float() argument must be a string or a real number, not 'NoneType'.Fixed a segmentation fault caused by item delegates being registered without a parent, leaving Qt holding a pointer to a garbage-collected object.
Fixed a circular import that broke
import ModanComponentsin a fresh interpreter.
Technical
Test suite grew from 1242 to 1404 passing (75 skipped)
New regression suites: import cycles (checked in a fresh subprocess, since in-process module caching hides them), landmark parsing, cell validation, sentinel import, count consistency, unimputable landmarks, count display
Single implementations for landmark counting (
landmark_position_count,find_landmark_count_mismatch) shared by every gate that can reject a datasetpre-commitinstalled and configured;AppDir/excluded from the whitespace hooks, which corrupted git symlinks on filesystems without symlink supportRuff pinned in
.pre-commit-config.yamlto match the installed version
[0.1.5] - 2026-07-18
Stabilisation release. A full code review (R01) corrected several statistical results, a structural refactor removed the last god-methods and dead modules, and an error-handling audit closed the paths where a failure could kill a window silently.
Note for existing analyses: the CVA and MANOVA fixes below change numeric output. Analyses saved before 0.1.5 may differ if re-run.
Added
Data Exploration plot
Optional data-point labels
Snap-to-point when picking shapes
Shape-preview snapping is now toggleable (“Snap to points”)
Object editing — landmark mode is entered automatically after calibration, and exactly one tool mode stays selected
Fixed
Statistics correctness
CVA: use pseudo-inverse for a singular within-group covariance matrix instead of failing or producing garbage
CVA: raw eigenvalues no longer overwritten by their percentages
CVA/MANOVA: the Z coordinate of 3D landmarks is no longer dropped when flattening shapes
MANOVA: variable truncation is surfaced instead of silently capping
Crashes and silent failures
Clicking the dataset tree and exporting an analysis to Excel could kill the window with no message; the export crash was traced to the Shapes sheet
Frozen (packaged) builds failed at startup with “Can’t determine version for pytz”
Preferences were intermittently reset — settings are now written atomically
Error-handling audit: 40+ user-triggered handlers across the main window, dialogs, file parsers, 3D model I/O and zip import now report failures instead of dying quietly
Data integrity
Multi-row database operations are wrapped in transactions, so a failure part-way through no longer leaves a half-written dataset
Corrected model field names in controller create/import paths
Class-level mutable attributes were shared between instances
Analysis results were silently not persisted when the analysis type was passed in lowercase
Interface
The Preferences dialog is scrollable, so it is usable on low-resolution monitors
Changed
Performance — eliminated N+1 queries in CVA/MANOVA group extraction and in dataset switching; vectorised average-shape and CVA covariance computation
Technical
Removed dead code:
ModanDialogs.py(2,539 lines) migrated to thedialogs/package, plus 6 unused modules and 8 stale build specsDe-duplicated the
MODEdictionary (15 copies → 1) and object-viewer constants intoMdConstantsDecomposed god-methods:
run_analysis,prepare_scatter_data,read_settings, and two dialog__init__s; moved dialog database/file I/O intoModanControllerAdded the
guard_slotdecorator for Qt signal handlersRepo-wide ruff lint/format clean; numpy pins reconciled to
>=2.0.0,<3.0.0CI: build numbers derived from commit count across all build workflows; Inno Setup pinned to 6.7.3
[0.1.5-beta.2] - 2025-11-03
Added
Object Preview Enhancements
Preview toolbar button icon (icons/Preview.png)
Smart button activation (enabled only when dataset is selected)
Consistent UI behavior with other dataset-related actions
Changed
UI Consistency Improvements
Preview button now follows same activation pattern as New Object, Export, and Analyze buttons
Preview button disabled when analysis is selected or no dataset loaded
Improved visual feedback with proper icon display
Fixed
Object Drag and Drop
Fixed
NameError: name 'CustomDrag' is not definedwhen dragging objects between datasetsAdded missing
CustomDragimport incomponents/widgets/table_view.pyFixed cursor not restoring to normal after drag operation completes
Implemented proper cursor stack cleanup using
QApplication.restoreOverrideCursor()Cursor now correctly reverts to arrow cursor after both successful and cancelled drag operations
Technical
Code Quality
Added cursor restoration in both
startDrag()andmouseReleaseEvent()methodsUsed while loop to clear all override cursors from Qt’s cursor stack
Improved drag operation robustness with proper cleanup
All drag widget tests passing (3/3)
Overall test coverage maintained at 99.6% (230/231 tests passing)
[0.1.5-beta.1] - 2025-10-08
Added (Phase 7 - Performance Testing & Scalability)
Comprehensive Performance Testing Infrastructure
Large-scale benchmarking tool (
scripts/benchmark_large_scale.py)CVA performance profiler (
scripts/profile_cva.py)Memory profiling system (
scripts/profile_memory.py)UI responsiveness tests (
scripts/test_ui_responsiveness.py)
Performance Validation (All Targets Exceeded)
Load performance: 18-200× better than targets
1000 objects load: 277ms (target: < 5s) - 18× faster
1000 objects PCA: 60ms (target: < 2s) - 33× faster
Memory efficiency: 125× better than target
1000 objects: 4.04MB peak (target: < 500MB)
Linear scaling: ~4KB per object
No memory leaks detected (2.7KB growth over 50 iterations)
UI responsiveness: 9-5091× better than targets
Widget creation: 12.63ms for 1000-row table (target: < 100ms) - 8× faster
Dataset loading: 536ms for 1000 objects (target: < 5s) - 9× faster
Progress updates: 152,746/sec (target: > 30/sec) - 5091× faster
Comprehensive User Documentation
Quick Start Guide (10-minute tutorial)
Complete User Guide (400+ lines covering all features)
Performance guide integrated with Phase 7 results
Troubleshooting section with solutions
Installation guide for all platforms
Build guide for developers
Release Preparation (Phase 8)
BUILD_GUIDE.md - Comprehensive build instructions
INSTALL.md - Platform-specific installation guides
Enhanced documentation structure
Changed
Code Quality Improvements
Extensive test coverage (1,240 tests, 93.5% pass rate)
Integration testing complete (Phase 6)
Component testing complete (Phase 5)
Performance profiling and validation (Phase 7)
Performance
Validated Scalability
Tested up to 2,000 objects
All operations linear O(n) scaling
Production-ready for datasets of 100,000+ objects
Documentation
Complete Documentation Suite
User documentation for all skill levels
Developer guides and API references
Build and installation instructions
Performance expectations and best practices
[0.1.5-alpha.1] - 2025-09-11
Added
JSON+ZIP 데이터셋 패키징 시스템
완전한 데이터셋 백업 및 공유를 위한 새로운 export/import 형식
JSON schema v1.1 with 확장된 메타데이터 (wireframe, polygons, baseline, variables)
ZIP 패키징으로 이미지 및 3D 모델 파일 포함 지원
구조화된 파일 레이아웃 (dataset.json, images/, models/)
손실 없는 라운드트립 데이터 보존
보안 및 안정성 기능
Zip Slip 공격 방어 시스템
트랜잭션 기반 import (실패 시 자동 롤백)
파일 무결성 검증 (MD5 체크섬)
안전한 ZIP 압축 해제 (
safe_extract_zip())JSON 스키마 검증 및 에러 리포팅
새로운 API 함수들 (MdUtils.py)
serialize_dataset_to_json()- 데이터셋을 JSON 구조로 직렬화create_zip_package()- 파일 수집 및 ZIP 패키징import_dataset_from_zip()- 안전한 ZIP 기반 데이터셋 importcollect_dataset_files()- 데이터셋 관련 파일 경로 수집estimate_package_size()- 패키지 크기 추정validate_json_schema()- JSON 스키마 유효성 검사
사용자 인터페이스 개선
Export Dialog에 “JSON+ZIP Package” 옵션 추가
“Include image and model files” 토글 기능
실시간 파일 크기 추정 표시
Import Dialog에 JSON+ZIP 형식 지원 추가
진행률 추적 및 진행 상황 콜백
Changed
기존 export 형식 유지
TPS, NTS, Morphologika, CSV/Excel 형식 계속 지원
JSON+ZIP은 완전한 백업용 추가 옵션으로 제공
파일 명명 규칙 개선
ZIP 내부 파일은
<object_id>.<ext>형식으로 충돌 방지상대 경로 사용으로 플랫폼 독립성 확보
데이터베이스 처리 개선
중복 데이터셋 이름 자동 해결 (“Dataset (1)”, “Dataset (2)” 등)
변수 매핑 및 랜드마크 처리 최적화
Fixed
크로스 플랫폼 호환성
UTF-8 인코딩으로 한국어 파일명 지원
Windows, macOS, Linux 경로 처리 통일
파일 시스템 안전성 검증 추가
메모리 및 성능 최적화
대용량 파일 스트리밍 처리
임시 파일 안전한 정리 (context manager 사용)
에러 발생 시 부분 import 방지
[0.1.4] - 2025-09-10
Added
CI/CD 및 빌드 시스템
GitHub Actions 워크플로우 구축 (자동 빌드, 테스트, 릴리즈)
크로스 플랫폼 빌드 지원 (Windows, Linux, macOS)
PyInstaller 기반 자동화 빌드 스크립트 (
build.py)빌드 번호 시스템 및 버전 관리 중앙화 (
version.py)
테스트 인프라
pytest 기반 자동화 테스트 시스템 (229개 테스트, 13개 모듈)
테스트 카테고리: 단위, 통합, 성능, GUI, 워크플로우
CI 통합으로 PR시 자동 테스트 실행
테스트 커버리지 분석 도구 설정
UI/UX 기능
오버레이 드래그 및 코너 스냅 기능
오버레이 타이틀 표시
스플래시 스크린 (빌드 정보 및 저작권 표시)
3D 랜드마크 인덱스 표시 복원 (GLUT 사용)
툴바 버튼 상태 관리 개선
TreeView 사용성 개선
읽기 전용 열 컨텍스트 메뉴
문서화
한국어 README (
README.ko.md)개발 가이드 문서 (CLAUDE.md, GEMINI.md)
릴리즈 가이드 및 버전 관리 문서
Windows Defender 공지 문서
상세한 개발 로그 (devlog 디렉토리)
국제화 (i18n)
한국어 번역 대폭 개선
언어 설정 즉시 적용 기능
번역 파일 업데이트 (Modan2_ko.ts)
코드 인덱싱 시스템
소스코드 구조 분석 및 인덱싱 도구 구축
대화형 HTML 대시보드 (tools/visualize_index.py)
심볼 검색 및 의존성 분석 도구
Changed
코드 구조 개선
새로운 모듈 분리:
ModanController.py,MdHelpers.py,MdConstants.py,ModanWidgets.py설정 관리를 QSettings에서 JSON으로 마이그레이션
로깅 시스템 전환 (print문을 logging 모듈로)
에러 핸들링 강화 (체계적인 try-catch 구조)
저작권 정보 동적 연도 업데이트
의존성 업데이트
NumPy 2.0+ 지원 (OpenGL 호환성 문제 해결)
Python 3.12 지원
요구사항 파일 정리 및 크로스 플랫폼 지원
데이터 분석 개선
PCA/CVA 분석 시스템 재구조화
회귀 분석과 산점도 분리
Data Exploration Dialog 재구성
속성(property)에서 변수(variable)로 용어 통일
Fixed
분석 기능 개선
CVA/MANOVA 변수 선택 오류 수정: 선택된 그룹화 변수가 분석 함수에 제대로 전달되지 않던 심각한 문제 해결
분석 정확도 향상: CVA 및 MANOVA 계산에 올바른 범주형 변수가 사용되도록 변수 인덱싱 수정
데이터 검증 강화: 분석 실행 전 그룹화 변수 요구사항에 대한 적절한 검사 추가
주요 버그 수정
PCA 분석 일관성 및 차원 문제 해결
Reset Pose 기능 완전 복구
Windows Defender 오탐 문제 완화
OpenGL/GLUT와 matplotlib 호환성 문제 해결
Linux/WSL Qt 플랫폼 플러그인 오류 수정
CI/CD 테스트 XIO fatal 오류 해결
썸네일 동기화 문제 (WSL 특정)
landmark_count 의존성 제거 및 동적 계산
2D 객체 뷰 마이너 버그 수정
데이터셋 저장 모니터링 코드 개선
shape retrieval 문제 해결 (currentData vs currentIndex)
와이어프레임 랜드마크 인덱스 불일치 수정
빌드 및 배포
InnoSetup 출력 디렉토리 수정
Linux AppImage 생성 프로세스 개선
macOS 빌드 아티팩트 패턴 수정
Anaconda Python 호환성 개선
테스트 수정
PyQt5 호환성 문제 해결
테스트 데이터에 그룹화 변수 추가
Removed
하드코딩된 Modan2.spec 파일 (동적 생성으로 전환)
레거시 테스트 스크립트 (pytest로 마이그레이션)
[0.1.3] - 2024-06-21
Added
초기 안정 버전 릴리즈
기본 형태 분석 기능
2D/3D 랜드마크 지원
TPS, NTS, OBJ, PLY, STL 파일 형식 지원
기본 통계 분석 기능 (PCA, CVA, MANOVA)
[0.1.2] - 2024-05-30
Added
3D 모델 지원 (OBJ, PLY, STL)
2D/3D 뷰어 개선
데이터셋 계층 구조
[0.1.1] - 2024-04-12
Added
기본 형태측정 분석
프로크루스테스 중첩정렬
간단한 데이터 관리
[0.1.0] - 2024-03-01
Added
초기 개발 버전
개념 증명(proof of concept)