Motomaticas is a modular open-source mathematical engine that solves the fragmentation problem in mathematical calculation tools. Through dynamic dependency injection, it allows programmers and educators to create, share, and reuse mathematical modules without needing to develop the entire infrastructure from scratch or learn specialized languages like MATLAB or Mathematica.
The project stems from the need to democratize the development of interactive mathematical tools. Traditionally, creating a custom scientific calculator requires:
- Mastery of specialized mathematical languages (high training time investment)
- Complete implementation of operation hierarchy
- Development of parsing and evaluation system
- GUI creation from scratch
Motomaticas abstracts all this complexity through a mathematical plugin system where developers only focus on the specific logic of their operation or unit.
Main interface showing complex operations with 3D vectors and real numbers

- 🔌 Dynamic Dependency Injection: Plugin system to add units and operations without modifying base code
- 📊 Automatic Operation Hierarchy: Correct resolution according to mathematical order (PEMDAS) with 4 priority levels
- 📚 Auto-Generated Explanations: Each operation generates human-readable documentation of its process
- 🎨 Modular Interface: Independent panels (Operations, Procedure, Notes, Whiteboard, Visualization)
- 💾 Note-Taking System: Complete session serialization for sharing and reuse
- ⚡ Divide & Conquer Algorithm: Recursive resolution of sub-operations with O(n log n) complexity
- 🧩 Extreme Polymorphism: 100% abstraction-based architecture (ObjetoMatematico base)
- 🔄 Nested Expression Evaluation: Support for operations with multiple levels of parentheses
- 🎯 Type Safety: Runtime operand validation system
- 📦 Complete Serialization: State persistence through Serializable
ObjetoMatematico (Abstract)
├── UnidadMatematica (Abstract)
│ ├── Vector3D
│ ├── UnidadNumerosRacionales
│ └── [Other injectable units]
├── OperacionMatematica (Abstract)
│ ├── Suma
│ ├── Resta
│ ├── Multiplicacion
│ ├── Division
│ └── [Other injectable operations]
└── FuncionMatematica (Abstract)
└── [Injectable functions]
The engine implements a 4-level priority system:
- Level 4 - Parentheses and Functions: Encapsulated operations
{...}or functionsf(x) - Level 3 - Powers: Exponentiation, roots
- Level 2 - Multiplication/Division: Second-order operations
- Level 1 - Addition/Subtraction: First-order operations and final result
public class motomaticas {
private ArrayList<UnidadMatematica> unidadesMatematicasUsadas;
private ArrayList<OperacionMatematica> operacionesMatematicasUsadas;
private ArrayList<FuncionMatematica> funcionesMatematicasUsadas;
public void importarUnidadesMatematicas(UnidadMatematica... UnidadesMatematicas) {
for(UnidadMatematica newUnidad : UnidadesMatematicas) {
unidadesMatematicasUsadas.add(newUnidad);
}
}
}This pattern enables runtime injection without recompilation.
Each class has a single, well-defined responsibility:
OperacionGeneral: Only resolves operationsObjetoMatematico: Only defines base structureUnidadMatematica: Only represents quantifiable units
System open for extension through inheritance, closed for modification:
public abstract class UnidadMatematica extends ObjetoMatematico {
public abstract UnidadMatematica crearUnidad(JFrame padre);
public abstract String toString();
public abstract String toStringReducido();
}Developers extend without modifying the core engine.
Any subtype of ObjetoMatematico is interchangeable:
List<ObjetoMatematico> operacion = new LinkedList<>();
operacion.add(new Vector3D());
operacion.add(new Suma());
operacion.add(new UnidadNumerosRacionales());Specialized interfaces avoid unnecessary dependencies:
public interface operacionesMatematicasGenericasInterface {
class llaveIzquierda extends OperacionMatematica { }
class llaveDerecha extends OperacionMatematica { }
}Architecture core: Engine depends on abstractions, not concrete implementations:
public class OperacionGeneral {
private List<ObjetoMatematico> ObjetosMatematicos;
public TipoDeErrorMatematico CalcularOperacion() {
}
}The engine works exclusively with ObjetoMatematico, allowing injection of any type.
Template Method: Abstract classes define structure, subclasses implement details
Factory Pattern: Dynamic creation of mathematical objects by type
Iterator Pattern: Operation traversal using CopyOnWriteArrayList
Complexity: O(n log n) where n is the number of mathematical objects Strategy: Divide & Conquer with recursion
private TipoDeErrorMatematico ResolverPrimerOrden() {
Repetidor: while (ObjetosMatematicos.size() > 0) {
for (Iterator<ObjetoMatematico> IteradorObjetosMatematicos0 =
ObjetosMatematicos.iterator();
IteradorObjetosMatematicos0.hasNext();) {
ObjetoMatematico ObjetoActual = IteradorObjetosMatematicos0.next();
switch (ObjetoActual.getTipoDeObjetoMatematico()) {
case Unidad:
ObjetosMatematicos_PrimerOrden.add(ObjetoActual);
ObjetosMatematicos.remove(ObjetoActual);
break;
case Operacion:
if (((OperacionMatematica) ObjetoActual).getConLlave()) {
ObjetoMatematico ParentesisCierre = BuscarParentesisCierre(ObjetoActual);
if (ParentesisCierre != null) {
OperacionGeneral subOperacion = new OperacionGeneral(
SubdividirOperacion(ObjetosMatematicos, ObjetoActual, ParentesisCierre)
);
TipoDeErrorMatematico PosibleError = subOperacion.CalcularOperacion();
if (PosibleError == null) {
ObjetosMatematicos_PrimerOrden.add(
((OperacionMatematica) ObjetoActual)
.calcularOperacion((UnidadMatematica) subOperacion.getResultado())
);
continue Repetidor;
} else {
return PosibleError;
}
} else {
return TipoDeErrorMatematico.ParentesisAbiertos;
}
}
break;
}
}
}
return null;
}Complexity: O(n) Algorithm: Parenthesis balance counter
private ObjetoMatematico BuscarParentesisCierre(ObjetoMatematico Inicio) {
int NumeroDeParentesis = 0;
for (int Index = ObjetosMatematicos.indexOf(Inicio);
Index < ObjetosMatematicos.size();
Index++) {
if (ObjetosMatematicos.get(Index).TipoDeObjetoMatematico == TipoObjetoMatematico.Operacion) {
OperacionMatematica operacion = (OperacionMatematica) ObjetosMatematicos.get(Index);
if (operacion.getConLlave()) {
if (operacion.getNombreObjetoMatematico()
.equals((new operacionesMatematicasGenericasInterface.llaveDerecha())
.getNombreObjetoMatematico()))
NumeroDeParentesis--;
else
NumeroDeParentesis++;
}
if (NumeroDeParentesis == 0) {
return ObjetosMatematicos.get(Index);
}
}
}
return null;
}Strategy: Recursion with pointer array to maintain state
private TipoDeErrorMatematico ResolverOrdenN(
List<ObjetoMatematico> ListaDeOrdenPrevia,
List<ObjetoMatematico> ListaDeOrdenN[],
int prioridad) {
List<ObjetoMatematico>[] ListaArraySegundoOrden = new List[1];
ListaArraySegundoOrden[0] = new CopyOnWriteArrayList<ObjetoMatematico>();
TipoDeErrorMatematico posibleError = ResolverSegundoOrden(
ListaDeOrdenPrevia,
ListaArraySegundoOrden,
prioridad
);
ListaDeOrdenN[0].clear();
ListaDeOrdenN[0].addAll(ListaArraySegundoOrden[0]);
return posibleError;
}Best case: O(n) - Linear operation without parentheses Average case: O(n log n) - Operations with moderate nesting Worst case: O(n²) - Deep nesting with multiple recursive sub-operations
Implemented optimizations:
CopyOnWriteArrayListfor thread-safe iteration- Operator search by priority avoiding complete traversals
- Sub-result reuse (implicit memoization)
Purpose: Core expression evaluation engine Implementation:
public class OperacionGeneral implements Serializable {
private List<ObjetoMatematico> ObjetosMatematicos;
private List<ObjetoMatematico> ObjetosMatematicos_PrimerOrden;
private List<ObjetoMatematico> ObjetosMatematicos_SegundoOrden;
private List<ObjetoMatematico> ObjetosMatematicos_TercerOrden;
private List<ObjetoMatematico> ObjetosMatematicos_CuartoOrden;
public TipoDeErrorMatematico CalcularOperacion() {
reset();
TipoDeErrorMatematico PrimerOrdenError = ResolverPrimerOrden();
if (PrimerOrdenError != null) return PrimerOrdenError;
List<ObjetoMatematico>[] punteroDeLista = new List[1];
punteroDeLista[0] = ObjetosMatematicos_SegundoOrden;
TipoDeErrorMatematico SegundoOrdenError =
ResolverOrdenN(ObjetosMatematicos_PrimerOrden, punteroDeLista, 3);
if (SegundoOrdenError != null) return SegundoOrdenError;
return null;
}
}Applied pattern: Template Method + Chain of Responsibility
Challenges overcome:
- Correct operator precedence handling
- Syntax error detection (unbalanced parentheses, consecutive operators)
- Lazy evaluation for performance optimization
Purpose: Mathematical plugin management Implementation:
public class motomaticas {
private ArrayList<UnidadMatematica> unidadesMatematicasUsadas;
private ArrayList<OperacionMatematica> operacionesMatematicasUsadas;
private ArrayList<FuncionMatematica> funcionesMatematicasUsadas;
public void importarUnidadesMatematicas(UnidadMatematica... UnidadesMatematicas) {
for(UnidadMatematica newUnidad : UnidadesMatematicas) {
unidadesMatematicasUsadas.add(newUnidad);
}
}
}Applied pattern: Dependency Injection + Registry Pattern
Interaction with other modules:
- Registers plugins in static global lists
- Provides centralized access to all available mathematical objects
- Manages application lifecycle
Purpose: Unified abstraction for all mathematical elements Implementation:
public abstract class ObjetoMatematico implements Serializable {
protected String ID;
public TipoObjetoMatematico TipoDeObjetoMatematico;
private String NombreObjetoMatematico;
protected String SimboloIdentificador;
public ObjetoMatematico(String nombreObjetoMatematico,
String SimboloIdentificador,
TipoObjetoMatematico TipoDeObjetoMatematicoio) {
this.ID = UUID.randomUUID().toString();
this.SimboloIdentificador = SimboloIdentificador;
this.NombreObjetoMatematico = nombreObjetoMatematico;
this.TipoDeObjetoMatematico = TipoDeObjetoMatematicoio;
}
public abstract String toStringReducido();
public abstract String toString();
}Applied pattern: Abstract Factory
Challenges overcome:
- Design of flexible hierarchy allowing any type of mathematical unit
- Unique identification system using UUID
- Serialization for session persistence
Purpose: UI panel control through flags Implementation:
public class configuracionProyecto {
public static boolean panelGrafico;
public static boolean panelPizzarra;
public static boolean panelNotas;
public static boolean panelOperaciones;
public static boolean panelProcesoExplicadoMatematico;
public static boolean panelVisualizar;
public static void activarPanelOperaciones(boolean panelOperaciones) {
configuracionProyecto.panelOperaciones = panelOperaciones;
}
}Applied pattern: Implicit Singleton (static variables)
Interaction:
- Controls UI component visibility from entry point
- Allows modular interface customization
- Reduces coupling between visualization modules
- Java 8+: Primary language, object-oriented
- Swing/AWT: Cross-platform GUI libraries
- Java Serialization: Object persistence
Why Java?
- Cross-platform (Write Once, Run Anywhere)
- Wide adoption in education
- Mature library ecosystem
- Strong typing for safety in mathematical calculations
Why Swing/AWT?
- Standard libraries, no external dependencies
- Extensive documentation and large community
- Native cross-platform Look & Feel
- Integrated Drag & Drop for interactive UX
Motomaticas/
├── src/
│ ├── Motomaticas/
│ │ ├── ObjetosLogicos/
│ │ │ └── motorMatematico/
│ │ │ ├── ObjetoMatematico.java
│ │ │ ├── OperacionGeneral.java
│ │ │ ├── operaciones/
│ │ │ ├── variables/
│ │ │ └── funciones/
│ │ ├── VentanasProyecto/
│ │ ├── RecursosCustomizados/
│ │ ├── ValoresDefault/
│ │ ├── motomaticas.java
│ │ └── configuracionProyecto.java
│ ├── ModulosImportados/
│ │ ├── NumerosReales/
│ │ └── Vector3D/
│ └── Principal/
│ └── main.java
├── resources/
└── README.md
- JDK: 8 or higher
- RAM: 512 MB minimum
- OS: Windows, macOS, Linux (any system with JVM)
__ __ _ _ _
| \/ | ___ | |_ ___ _ __ ___ __ _| |_(_) ___ __ _ ___
| |\/| |/ _ \| __/ _ \| '_ ` _ \ / _` | __| |/ __/ _` / __|
| | | | (_) | || (_) | | | | | | (_| | |_| | (_| (_| \__ \
|_| |_|\___/ \__\___/|_| |_| |_|\__,_|\__|_|\___\__,_|___/
Modular mathematical tool developed with ❤️ in Java
⭐ If you find this project useful, consider giving it a star on GitHub