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

This commit is contained in:
Ad5001 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(); 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 } intersection(domain) { return this }
static import(frm) { return new EmptySet() }
static import(frm) { static import(frm) {
switch(frm.trim().toUpperCase()) { switch(frm.trim().toUpperCase()) {
case "R": case "R":
@ -128,8 +126,50 @@ class Domain {
case "*⁻": case "*⁻":
return Domain.RME return Domain.RME
break; 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: default:
return EmptySet() return new EmptySet()
break; break;
} }
} }
@ -195,25 +235,76 @@ class Interval extends Domain {
return new Interval(begin.trim(), end.trim(), openBegin, openEnd) 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 { class SpecialDomain extends Domain {
constructor(values) { // 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() super()
var newVals = [] this.displayName = displayName
values.forEach(function(value){ this.isValid = isValid
newVals.push(new Expression(value.toString())) this.nextValue = next
}) this.prevValue = previous
this.values = newVals 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) { includes(x) {
@ -223,6 +314,29 @@ class DomainSet extends Domain {
return false 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() { toString() {
return "{" + this.values.join(";") + "}" return "{" + this.values.join(";") + "}"
} }
@ -239,7 +353,7 @@ class DomainSet extends Domain {
} }
var notIncludedValues = [] var notIncludedValues = []
for(var i = 0; i < this.values.length; i++) { 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 instanceof Interval) {
if(domain.begin.execute() == value && domain.openBegin) { if(domain.begin.execute() == value && domain.openBegin) {
domain.openBegin = false domain.openBegin = false
@ -266,7 +380,7 @@ class DomainSet extends Domain {
} }
var includedValues = [] var includedValues = []
for(var i = 0; i < this.values.length; i++) { 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 instanceof Interval) {
if(domain.begin.execute() == value && !domain.openBegin) { if(domain.begin.execute() == value && !domain.openBegin) {
domain.openBegin = false domain.openBegin = false
@ -325,7 +439,6 @@ class UnionDomain extends Domain {
if(domains.length == 1) domains = frm.trim().split("U") // Fallback if(domains.length == 1) domains = frm.trim().split("U") // Fallback
var dom1 = parseDomain(domains.pop()) var dom1 = parseDomain(domains.pop())
var dom2 = parseDomain(domains.join('')) var dom2 = parseDomain(domains.join(''))
console.log('Union', dom1, dom2)
return dom1.union(dom2) return dom1.union(dom2)
} }
} }
@ -365,7 +478,7 @@ class IntersectionDomain extends Domain {
static import(frm) { static import(frm) {
var domains = frm.trim().split("∩") var domains = frm.trim().split("∩")
var dom1 = parseDomain(domains.pop()) var dom1 = parseDomain(domains.pop())
var dom2 = parseDomain(domains.join('')) var dom2 = parseDomain(domains.join(''))
return dom1.intersection(dom2) return dom1.intersection(dom2)
} }
} }
@ -397,14 +510,41 @@ class MinusDomain extends Domain {
Domain.RE = new MinusDomain("R", "{0}") Domain.RE = new MinusDomain("R", "{0}")
Domain.RE.displayName = "*" 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 = [] var refedDomains = []
function parseDomain(domain) { function parseDomain(domain) {
if(!domain.includes(')') && !domain.includes('(')) return parseDomainSimple(domain) if(!domain.includes(')') && !domain.includes('(')) return parseDomainSimple(domain)
var domStr var domStr
while((domStr = /\(([^)(]+)\)/.exec(domain)) !== null) { while((domStr = /\(([^)(]+)\)/.exec(domain)) !== null) {
var dom = parseDomainSimple(domStr[1]); var dom = parseDomainSimple(domStr[1].trim());
console.log(domain, domStr[0])
domain = domain.replace(domStr[0], 'D' + refedDomains.length) domain = domain.replace(domStr[0], 'D' + refedDomains.length)
refedDomains.push(dom) refedDomains.push(dom)
} }
@ -412,12 +552,14 @@ function parseDomain(domain) {
} }
function parseDomainSimple(domain) { function parseDomainSimple(domain) {
if(domain[0] == 'D') return refedDomains[parseInt(domain.substr(1))] domain = domain.trim()
if(domain.indexOf("U") >= 0 || domain.indexOf("") >= 0) return UnionDomain.import(domain) if(domain.includes("U") || domain.includes("")) return UnionDomain.import(domain)
if(domain.indexOf("∩") >= 0) return IntersectionDomain.import(domain) if(domain.includes("∩")) return IntersectionDomain.import(domain)
if(domain.indexOf("") >= 0 || domain.indexOf("\\") >= 0) return MinusDomain.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.charAt(0) == "{" && domain.charAt(domain.length -1) == "}") return DomainSet.import(domain)
if(domain.indexOf("]") >= 0 || domain.indexOf("[") >= 0) return Interval.import(domain) if(domain.includes("]") || domain.includes("[")) return Interval.import(domain)
if(domain.toUpperCase().indexOf("R") >= 0 || domain.indexOf("") >= 0) return Domain.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() 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) canvas.drawLine(ctx, canvasX-pointSize/2, canvasY+pointSize/2, canvasX+pointSize/2, canvasY-pointSize/2)
break; break;
case '': case '':
canvas.drawLine(ctx, canvasX, canvasY-pointSize/2, canvasX, canvasY+pointSize/2) ctx.fillRect(canvasX-pointSize/2, canvasY-1, pointSize, 2)
canvas.drawLine(ctx, canvasX-pointSize/2, canvasY, canvasX+pointSize/2, canvasY) ctx.fillRect(canvasX-1, canvasY-pointSize/2, 2, pointSize)
break; break;
} }
var text = this.getLabel() var text = this.getLabel()
@ -265,10 +265,10 @@ class Function extends ExecutableObject {
var posY = canvas.y2px(this.expression.execute(this.labelX)) var posY = canvas.y2px(this.expression.execute(this.labelX))
switch(this.labelPosition) { switch(this.labelPosition) {
case 'above': case 'above':
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY-textSize.height) canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height)
break; break;
case 'below': case 'below':
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY+textSize.height) canvas.drawVisibleText(ctx, text, posX-textSize.width, posY+textSize.height)
break; break;
} }
@ -279,17 +279,43 @@ class Function extends ExecutableObject {
// Drawing small traits every 2px // Drawing small traits every 2px
var pxprecision = 2 var pxprecision = 2
var previousX = canvas.px2x(0) var previousX = canvas.px2x(0)
var previousY = expr.execute(previousX) var previousY;
for(var px = pxprecision; px < canvas.canvasSize.width; px += pxprecision) { var draw = function(currentX) {
var currentX = canvas.px2x(px) }
var currentY = expr.execute(currentX) console.log('Drawing', inDomain, inDomain instanceof MathLib.SpecialDomain)
if((inDomain.includes(currentX) || inDomain.includes(previousX)) && if(inDomain instanceof MathLib.SpecialDomain && inDomain.moveSupported) {
(outDomain.includes(currentY) || outDomain.includes(previousY)) && previousX = inDomain.previous(previousX)
Math.abs(previousY-currentY)<100) { // 100 per 2px is a lot (probably inf to inf issue) if(previousX === null) previousX = inDomain.next(canvas.px2x(0))
canvas.drawLine(ctx, canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY)) 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 previousX = currentX
previousY = currentY 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
}
} }
} }
} }