New special domains (ℕ, ℤ and the likes), working with functions!

This commit is contained in:
Adsooi 2020-12-25 19:30:19 +01:00
parent 065b88bfd0
commit ef87b8362c
3 changed files with 225 additions and 47 deletions

View file

@ -209,5 +209,15 @@ Canvas {
ctx.stroke();
}
function drawDashedLine(ctx, x1, y1, x2, y2, dashPxSize = 10) {
var distance = Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))
var progPerc = dashPxSize/distance
ctx.beginPath();
ctx.moveTo(x1, y1);
for(var i = progPerc/2; i < 1; i += progPerc) {
ctx.lineTo(x1-(x1-x2)*i, y1-(y1-y2)*i)
ctx.moveTo(x1-(x1-x2)*(i+progPerc/2), y1-(y1-y2)*(i+progPerc/2))
}
ctx.stroke();
}
}

View file

@ -89,8 +89,6 @@ class Domain {
intersection(domain) { return this }
static import(frm) { return new EmptySet() }
static import(frm) {
switch(frm.trim().toUpperCase()) {
case "R":
@ -128,8 +126,50 @@ class Domain {
case "*⁻":
return Domain.RME
break;
case "":
case "N":
case "ZP":
case "ℤ⁺":
return Domain.N
break;
case "NE":
case "NP":
case "N*":
case "N+":
case "*":
case "ℕ⁺":
case "ZPE":
case "ZEP":
case "Z+*":
case "Z*+":
case "ℤ⁺*":
case "*⁺":
return Domain.NE
break;
case "Z":
case "":
return Domain.Z
break;
case "ZM":
case "Z-":
case "ℤ⁻":
return Domain.ZM
break;
case "ZME":
case "ZEM":
case "Z-*":
case "Z*-":
case "ℤ⁻*":
case "*⁻":
return Domain.ZME
break;
case "ZE":
case "Z*":
case "*":
return Domain.ZE
break;
default:
return EmptySet()
return new EmptySet()
break;
}
}
@ -195,25 +235,76 @@ class Interval extends Domain {
return new Interval(begin.trim(), end.trim(), openBegin, openEnd)
}
}
Domain.R = new Interval(-Infinity,Infinity,true,true)
Domain.R.displayName = ""
Domain.RP = new Interval(0,Infinity,true,false)
Domain.RP.displayName = "ℝ⁺"
Domain.RM = new Interval(-Infinity,0,true,false)
Domain.RM.displayName = "ℝ⁻"
Domain.RPE = new Interval(0,Infinity,true,true)
Domain.RPE.displayName = "ℝ⁺*"
Domain.RME = new Interval(-Infinity,0,true,true)
Domain.RME.displayName = "ℝ⁻*"
class DomainSet extends Domain {
constructor(values) {
class SpecialDomain extends Domain {
// For special domains (N, Z...)
// isValidExpr is function returning true when number is in domain
// false when it isn't.
// nextValue provides the next positive value in the domain
// after the one given.
constructor(displayName, isValid, next = x => true, previous = x => true,
moveSupported = true) {
super()
var newVals = []
values.forEach(function(value){
newVals.push(new Expression(value.toString()))
})
this.values = newVals
this.displayName = displayName
this.isValid = isValid
this.nextValue = next
this.prevValue = previous
this.moveSupported = moveSupported
}
includes(x) {
if(typeof x == 'string') x = executeExpression(x)
return this.isValid(x)
}
next(x) {
if(typeof x == 'string') x = executeExpression(x)
return this.nextValue(x)
}
previous(x) {
if(typeof x == 'string') x = executeExpression(x)
return this.prevValue(x)
}
toString() {
return this.displayName
}
union(domain) {
if(domain instanceof EmptySet) return this
if(domain instanceof DomainSet) return domain.union(this)
if(domain instanceof UnionDomain) return new UnionDomain(this, domain)
if(domain instanceof IntersectionDomain) return new UnionDomain(this, domain)
if(domain instanceof MinusDomain) return new UnionDomain(this, domain)
if(domain instanceof Interval) return new UnionDomain(this, domain)
}
intersection(domain) {
if(domain instanceof EmptySet) return domain
if(domain instanceof DomainSet) return domain.intersection(this)
if(domain instanceof UnionDomain) return new IntersectionDomain(this, domain)
if(domain instanceof IntersectionDomain) return new IntersectionDomain(this, domain)
if(domain instanceof MinusDomain) return new IntersectionDomain(this, domain)
if(domain instanceof Interval) return new IntersectionDomain(this, domain)
}
}
class DomainSet extends SpecialDomain {
constructor(values) {
super('', x => true, x => x, true)
console.log(values)
var newVals = {}
this.executedValues = []
for(var i = 0; i < values.length; i++) {
var expr = new Expression(values[i].toString())
var ex = expr.execute()
newVals[ex] = expr
this.executedValues.push(ex)
}
this.executedValues.sort((a,b) => a-b)
this.values = this.executedValues.map(val => newVals[val])
}
includes(x) {
@ -223,6 +314,29 @@ class DomainSet extends Domain {
return false
}
next(x) {
if(typeof x == 'string') x = executeExpression(x)
if(x < this.executedValues[0]) return this.executedValues[0]
for(var i = 1; i < this.values.length; i++) {
var prevValue = this.executedValues[i-1]
var value = this.executedValues[i]
if(x >= prevValue && x < value) return value
}
return null
}
previous(x) {
if(typeof x == 'string') x = executeExpression(x)
if(x > this.executedValues[this.executedValues.length-1])
return this.executedValues[this.executedValues.length-1]
for(var i = 1; i < this.values.length; i++) {
var prevValue = this.executedValues[i-1]
var value = this.executedValues[i]
if(x > prevValue && x <= value) return prevValue
}
return null
}
toString() {
return "{" + this.values.join(";") + "}"
}
@ -239,7 +353,7 @@ class DomainSet extends Domain {
}
var notIncludedValues = []
for(var i = 0; i < this.values.length; i++) {
var value = this.values[i].execute()
var value = this.executedValues[i]
if(domain instanceof Interval) {
if(domain.begin.execute() == value && domain.openBegin) {
domain.openBegin = false
@ -266,7 +380,7 @@ class DomainSet extends Domain {
}
var includedValues = []
for(var i = 0; i < this.values.length; i++) {
var value = this.values[i].execute()
var value = this.executedValues[i]
if(domain instanceof Interval) {
if(domain.begin.execute() == value && !domain.openBegin) {
domain.openBegin = false
@ -325,7 +439,6 @@ class UnionDomain extends Domain {
if(domains.length == 1) domains = frm.trim().split("U") // Fallback
var dom1 = parseDomain(domains.pop())
var dom2 = parseDomain(domains.join(''))
console.log('Union', dom1, dom2)
return dom1.union(dom2)
}
}
@ -365,7 +478,7 @@ class IntersectionDomain extends Domain {
static import(frm) {
var domains = frm.trim().split("∩")
var dom1 = parseDomain(domains.pop())
var dom2 = parseDomain(domains.join(''))
var dom2 = parseDomain(domains.join(''))
return dom1.intersection(dom2)
}
}
@ -397,14 +510,41 @@ class MinusDomain extends Domain {
Domain.RE = new MinusDomain("R", "{0}")
Domain.RE.displayName = "*"
Domain.R = new Interval(-Infinity,Infinity,true,true)
Domain.R.displayName = ""
Domain.RP = new Interval(0,Infinity,true,false)
Domain.RP.displayName = "ℝ⁺"
Domain.RM = new Interval(-Infinity,0,true,false)
Domain.RM.displayName = "ℝ⁻"
Domain.RPE = new Interval(0,Infinity,true,true)
Domain.RPE.displayName = "ℝ⁺*"
Domain.RME = new Interval(-Infinity,0,true,true)
Domain.RME.displayName = "ℝ⁻*"
Domain.N = new SpecialDomain('', x => x%1==0 && x >= 0,
x => Math.max(Math.floor(x)+1, 0),
x => Math.max(Math.ceil(x)-1, 0))
Domain.NE = new SpecialDomain('*', x => x%1==0 && x > 0,
x => Math.max(Math.floor(x)+1, 1),
x => Math.max(Math.ceil(x)-1, 1))
Domain.Z = new SpecialDomain('', x => x%1==0, x => Math.floor(x)+1, x => Math.ceil(x)-1)
Domain.ZE = new SpecialDomain('*', x => x%1==0 && x != 0,
x => Math.floor(x)+1 == 0 ? Math.floor(x)+2 : Math.floor(x)+1,
x => Math.ceil(x)-1 == 0 ? Math.ceil(x)-2 : Math.ceil(x)-1)
Domain.ZM = new SpecialDomain('ℤ⁻', x => x%1==0 && x <= 0,
x => Math.min(Math.floor(x)+1, 0),
x => Math.min(Math.ceil(x)-1, 0))
Domain.ZME = new SpecialDomain('ℤ⁻*', x => x%1==0 && x < 0,
x => Math.min(Math.floor(x)+1, -1),
x => Math.min(Math.ceil(x)-1, -1))
var refedDomains = []
function parseDomain(domain) {
if(!domain.includes(')') && !domain.includes('(')) return parseDomainSimple(domain)
var domStr
while((domStr = /\(([^)(]+)\)/.exec(domain)) !== null) {
var dom = parseDomainSimple(domStr[1]);
console.log(domain, domStr[0])
var dom = parseDomainSimple(domStr[1].trim());
domain = domain.replace(domStr[0], 'D' + refedDomains.length)
refedDomains.push(dom)
}
@ -412,12 +552,14 @@ function parseDomain(domain) {
}
function parseDomainSimple(domain) {
if(domain[0] == 'D') return refedDomains[parseInt(domain.substr(1))]
if(domain.indexOf("U") >= 0 || domain.indexOf("") >= 0) return UnionDomain.import(domain)
if(domain.indexOf("∩") >= 0) return IntersectionDomain.import(domain)
if(domain.indexOf("") >= 0 || domain.indexOf("\\") >= 0) return MinusDomain.import(domain)
domain = domain.trim()
if(domain.includes("U") || domain.includes("")) return UnionDomain.import(domain)
if(domain.includes("∩")) return IntersectionDomain.import(domain)
if(domain.includes("") || domain.includes("\\")) return MinusDomain.import(domain)
if(domain.charAt(0) == "{" && domain.charAt(domain.length -1) == "}") return DomainSet.import(domain)
if(domain.indexOf("]") >= 0 || domain.indexOf("[") >= 0) return Interval.import(domain)
if(domain.toUpperCase().indexOf("R") >= 0 || domain.indexOf("") >= 0) return Domain.import(domain)
if(domain.includes("]") || domain.includes("[")) return Interval.import(domain)
if(["R", "", "N", "", "Z", ""].some(str => domain.toUpperCase().includes(str)))
return Domain.import(domain)
if(domain[0] == 'D') return refedDomains[parseInt(domain.substr(1))]
return new EmptySet()
}

View file

@ -158,8 +158,8 @@ class Point extends DrawableObject {
canvas.drawLine(ctx, canvasX-pointSize/2, canvasY+pointSize/2, canvasX+pointSize/2, canvasY-pointSize/2)
break;
case '':
canvas.drawLine(ctx, canvasX, canvasY-pointSize/2, canvasX, canvasY+pointSize/2)
canvas.drawLine(ctx, canvasX-pointSize/2, canvasY, canvasX+pointSize/2, canvasY)
ctx.fillRect(canvasX-pointSize/2, canvasY-1, pointSize, 2)
ctx.fillRect(canvasX-1, canvasY-pointSize/2, 2, pointSize)
break;
}
var text = this.getLabel()
@ -265,10 +265,10 @@ class Function extends ExecutableObject {
var posY = canvas.y2px(this.expression.execute(this.labelX))
switch(this.labelPosition) {
case 'above':
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY-textSize.height)
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height)
break;
case 'below':
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY+textSize.height)
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY+textSize.height)
break;
}
@ -279,17 +279,43 @@ class Function extends ExecutableObject {
// Drawing small traits every 2px
var pxprecision = 2
var previousX = canvas.px2x(0)
var previousY = expr.execute(previousX)
for(var px = pxprecision; px < canvas.canvasSize.width; px += pxprecision) {
var currentX = canvas.px2x(px)
var currentY = expr.execute(currentX)
if((inDomain.includes(currentX) || inDomain.includes(previousX)) &&
(outDomain.includes(currentY) || outDomain.includes(previousY)) &&
Math.abs(previousY-currentY)<100) { // 100 per 2px is a lot (probably inf to inf issue)
canvas.drawLine(ctx, canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY))
var previousY;
var draw = function(currentX) {
}
console.log('Drawing', inDomain, inDomain instanceof MathLib.SpecialDomain)
if(inDomain instanceof MathLib.SpecialDomain && inDomain.moveSupported) {
previousX = inDomain.previous(previousX)
if(previousX === null) previousX = inDomain.next(canvas.px2x(0))
previousY = expr.execute(previousX)
while(previousX !== null && canvas.x2px(previousX) < canvas.canvasSize.width) {
var currentX = inDomain.next(previousX)
var currentY = expr.execute(currentX)
if(currentX === null) break;
if((inDomain.includes(currentX) || inDomain.includes(previousX)) &&
(outDomain.includes(currentY) || outDomain.includes(previousY))) {
canvas.drawDashedLine(ctx, canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY))
ctx.fillRect(canvas.x2px(previousX)-5, canvas.y2px(previousY)-1, 10, 2)
ctx.fillRect(canvas.x2px(previousX)-1, canvas.y2px(previousY)-5, 2, 10)
}
previousX = currentX
previousY = currentY
previousX = currentX
previousY = currentY
}
// Drawing the last cross
ctx.fillRect(canvas.x2px(previousX)-5, canvas.y2px(previousY)-1, 10, 2)
ctx.fillRect(canvas.x2px(previousX)-1, canvas.y2px(previousY)-5, 2, 10)
} else {
previousY = expr.execute(previousX)
for(var px = pxprecision; px < canvas.canvasSize.width; px += pxprecision) {
var currentX = canvas.px2x(px)
var currentY = expr.execute(currentX)
if((inDomain.includes(currentX) || inDomain.includes(previousX)) &&
(outDomain.includes(currentY) || outDomain.includes(previousY)) &&
Math.abs(previousY-currentY)<100) {
canvas.drawLine(ctx, canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY))
}
previousX = currentX
previousY = currentY
}
}
}
}