Equation Solver
Equation Solver
The context provides a constraint-based system for positioning points. Define relationships between points, and the solver computes the coordinates.
Creating a Context
local h = require("hobby")
local ctx = h.context()Point Variables
known(x, y)
Creates a point with known coordinates.
local z0 = ctx:known(0, 0)
local z1 = ctx:known(100, 50)unknown()
Creates a point with unknown coordinates (to be solved).
local mid = ctx:unknown()point() / points(n)
Aliases for creating unknown points.
local p = ctx:point() -- single unknown
local pts = ctx:points(5) -- array of 5 unknownsConstraints
Equality Constraints
ctx:eq(v, h.point(50, 50)) -- v = (50, 50)
ctx:eqx(v, 50) -- v.x = 50
ctx:eqy(v, 30) -- v.y = 30
ctx:eqvar(a, b) -- a = b
ctx:eqvarx(a, b) -- a.x = b.x
ctx:eqvary(a, b) -- a.y = b.yMidpoint
ctx:midpoint(m, a, b) -- m = midpoint of a and b
local m = ctx:midpointof(a, b) -- convenience: returns new pointInterpolation (Between)
ctx:between(p, a, b, 0.25) -- p = 0.25[a,b] (quarter way)
local p = ctx:betweenat(a, b, 0.75) -- convenience: returns new pointCollinear
ctx:collinear(p, a, b) -- p lies on line through a and bIntersection
ctx:intersection(p, a1, a2, b1, b2) -- p = intersection of lines
local p = ctx:intersectionof(a1, a2, b1, b2) -- convenience versionVector Arithmetic
ctx:sum(r, a, b) -- r = a + b
ctx:diff(r, a, b) -- r = a - b
ctx:scaled(r, v, 2) -- r = 2 * vSolving
After defining constraints, call solve():
ctx:solve()Accessing Results
After solving:
print(v.x, v.y) -- direct access
local x, y = v:xy() -- as tuple
local p = v:point() -- as hobby pointBuilding Paths with Variables
The context can create path builders that reference variables:
local path = ctx:path()
:movetovar(z0)
:curvetovar(z1)
:linetovar(z2)
:build()Complete Example
local h = require("hobby")
local ctx = h.context()
-- Define rectangle corners
local z0 = ctx:known(0, 0) -- bottom-left
local z1 = ctx:known(100, 0) -- bottom-right
local z2 = ctx:known(100, 60) -- top-right
local z3 = ctx:known(0, 60) -- top-left
-- Derived points
local mid_bottom = ctx:midpointof(z0, z1)
local mid_top = ctx:midpointof(z3, z2)
local center = ctx:midpointof(mid_bottom, mid_top)
local third = ctx:betweenat(z0, z1, 1/3)
-- Intersection of diagonals
local diag_center = ctx:intersectionof(z0, z2, z1, z3)
-- Solve
ctx:solve()
-- Print results
print(string.format("Center: (%.1f, %.1f)", center.x, center.y))
print(string.format("1/3 point: (%.1f, %.1f)", third.x, third.y))
-- Draw
local rect = h.path()
:moveto(z0:point())
:lineto(z1:point())
:lineto(z2:point())
:lineto(z3:point())
:cycle()
:stroke("black")
:build()
local diag1 = h.path()
:moveto(z0:point())
:lineto(z2:point())
:evenly()
:stroke("gray")
:build()
local diag2 = h.path()
:moveto(z1:point())
:lineto(z3:point())
:evenly()
:stroke("gray")
:build()
local centerMark = h.fullcircle()
:scaled(6)
:shifted(center.x, center.y)
:fill("red")
h.svg()
:padding(10)
:add(rect)
:add(diag1)
:add(diag2)
:add(centerMark)
:write("context.svg")Triangle Centroid Example
local h = require("hobby")
local ctx = h.context()
-- Triangle vertices
local a = ctx:known(0, 0)
local b = ctx:known(80, 0)
local c = ctx:known(40, 70)
-- Midpoints of sides (for medians)
local mAB = ctx:known(40, 0)
local mBC = ctx:known(60, 35)
local mCA = ctx:known(20, 35)
-- Centroid: intersection of medians
local centroid = ctx:intersectionof(a, mBC, b, mCA)
ctx:solve()
print(string.format("Centroid: (%.1f, %.1f)", centroid.x, centroid.y))
-- Expected: (40.0, 23.3) = average of vertices