bugfixes
This commit is contained in:
@@ -3,8 +3,8 @@
|
|||||||
import {invokeCallback, mixin} from "@/common.js";
|
import {invokeCallback, mixin} from "@/common.js";
|
||||||
import {chart, createShape, deleteShapeId, dragging, draggingShapeIds, drawShape, widget} from "@/charts/chart.js";
|
import {chart, createShape, deleteShapeId, dragging, draggingShapeIds, drawShape, widget} from "@/charts/chart.js";
|
||||||
import {unique} from "@/misc.js";
|
import {unique} from "@/misc.js";
|
||||||
import {useChartOrderStore} from "@/orderbuild.js";
|
import {allocationText, useChartOrderStore} from "@/orderbuild.js";
|
||||||
import model from "color";
|
import Color from "color";
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -72,11 +72,20 @@ export class Shape {
|
|||||||
if (onDelete !== null )
|
if (onDelete !== null )
|
||||||
this.onDelete = onDelete
|
this.onDelete = onDelete
|
||||||
|
|
||||||
|
//
|
||||||
// Model values handled by this base class
|
// Model values handled by this base class
|
||||||
this.model.color = null
|
//
|
||||||
this.model.lineColor = null
|
|
||||||
this.model.textColor = null
|
|
||||||
|
|
||||||
|
this.model.color = null
|
||||||
|
// if allocation is set, a percentage is displayed
|
||||||
|
this.model.allocation = null
|
||||||
|
// if maxAllocation is set, the line color (but not text color) is shaded down based on allocation
|
||||||
|
this.model.maxAllocation = null
|
||||||
|
// both amount and amountSymbol must be set in order to display amount text
|
||||||
|
this.model.amount = null
|
||||||
|
this.model.amountSymbol = null
|
||||||
|
|
||||||
|
// LEAF SUBCLASSES MUST CALL setModel(model) AFTER ALL CONSTRUCTION.
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -84,14 +93,53 @@ export class Shape {
|
|||||||
//
|
//
|
||||||
|
|
||||||
setModel(model) {
|
setModel(model) {
|
||||||
if (model.textColor || model.lineColor) {
|
console.log('shape setModel', model)
|
||||||
if (model.textColor)
|
|
||||||
this.model.textColor = model.textColor
|
if (model.color)
|
||||||
if (model.lineColor)
|
this.model.color = model.color
|
||||||
this.model.lineColor = model.lineColor
|
if (model.allocation !== null && model.allocation !== undefined)
|
||||||
this.setProps(this.colorProps())
|
this.model.allocation = model.allocation
|
||||||
|
if (model.maxAllocation !== null && model.maxAllocation !== undefined)
|
||||||
|
this.model.maxAllocation = model.maxAllocation
|
||||||
|
if (model.amount !== null && model.amount !== undefined)
|
||||||
|
this.model.amount = model.amount
|
||||||
|
if (model.amountSymbol)
|
||||||
|
this.model.amountSymbol = model.amountSymbol
|
||||||
|
|
||||||
|
const newProps = {}
|
||||||
|
|
||||||
|
// text color
|
||||||
|
const color = this.model.color ? this.model.color : '#0044ff'
|
||||||
|
newProps.textcolor = color
|
||||||
|
|
||||||
|
// line color
|
||||||
|
if (this.model.allocation && this.model.maxAllocation) {
|
||||||
|
const w = this.model.allocation / this.model.maxAllocation
|
||||||
|
if (!w)
|
||||||
|
newProps.linecolor = 'rgba(0,0,0,0)'
|
||||||
|
else {
|
||||||
|
// weighted line color
|
||||||
|
let c = new Color(color).rgb()
|
||||||
|
// the perceptual color of a weight follows Stevens's Power Law
|
||||||
|
// https://en.wikipedia.org/wiki/Stevens's_power_law
|
||||||
|
newProps.linecolor = c.alpha(Math.pow(w,0.67)).string()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// todo text
|
else
|
||||||
|
newProps.linecolor = color
|
||||||
|
|
||||||
|
// text label
|
||||||
|
const text = allocationText(this.model.allocation, this.model.amount, this.model.amountSymbol)
|
||||||
|
if (!text.length)
|
||||||
|
newProps.showLabel = false
|
||||||
|
else {
|
||||||
|
newProps.text = text
|
||||||
|
newProps.showLabel = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.debug && this.id)
|
||||||
|
console.log('newProps', chart.getShapeById(this.id).getProperties(), newProps)
|
||||||
|
this.setProps(newProps)
|
||||||
}
|
}
|
||||||
|
|
||||||
onModel(model, changedKeys) {} // model was changed by a TradingView user action
|
onModel(model, changedKeys) {} // model was changed by a TradingView user action
|
||||||
@@ -181,10 +229,19 @@ export class Shape {
|
|||||||
else if (dirtyPoints(this.tvPoints, points)) {
|
else if (dirtyPoints(this.tvPoints, points)) {
|
||||||
const s = this.tvShape();
|
const s = this.tvShape();
|
||||||
if (this.debug) console.log('adjusting tv points', s, this.tvPoints, points)
|
if (this.debug) console.log('adjusting tv points', s, this.tvPoints, points)
|
||||||
if (!this.beingDragged()) {
|
if (!dragging) {
|
||||||
if (this.debug) console.log('not dragging. use setPoints.')
|
if (this.debug) console.log('not dragging. use setPoints.')
|
||||||
s.setPoints(points)
|
s.setPoints(points)
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
if (this.debug) console.log('dragging. use QUIET setPoints.')
|
||||||
|
// quiet setPoints doesn't disturb tool editing mode
|
||||||
|
const i = s._pointsConverter.apiPointsToDataSource(points)
|
||||||
|
// s._model.startChangingLinetool(this._source)
|
||||||
|
s._model.changeLinePoints(s._source, i)
|
||||||
|
// s._model.endChangingLinetool(!0)
|
||||||
|
s._source.createServerPoints()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -198,12 +255,17 @@ export class Shape {
|
|||||||
if(this.id) {
|
if(this.id) {
|
||||||
const p = dirtyItems(this.tvProps, props)
|
const p = dirtyItems(this.tvProps, props)
|
||||||
this.tvProps = this.tvProps === null ? p : mixin(p, this.tvProps)
|
this.tvProps = this.tvProps === null ? p : mixin(p, this.tvProps)
|
||||||
|
if (this.debug) console.log('setting tv props', this.tvProps)
|
||||||
this.tvShape().setProperties(p)
|
this.tvShape().setProperties(p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onProps(props) { // the display properties of an existing shape were changed
|
onProps(props) { // the display properties of an existing shape were changed
|
||||||
this.updateModel({lineColor:props.linecolor, textColor:props.textcolor})
|
console.log('shape onProps', this.model, props)
|
||||||
|
if (props.textcolor && props.textcolor !== this.model.color)
|
||||||
|
this.updateModel({color:props.textcolor})
|
||||||
|
else if (props.linecolor && props.linecolor !== this.model.color)
|
||||||
|
this.updateModel({color:props.linecolor})
|
||||||
}
|
}
|
||||||
|
|
||||||
beingDragged() {
|
beingDragged() {
|
||||||
@@ -261,14 +323,14 @@ function dirtyKeys(propsA, propsB) {
|
|||||||
if (propsA===null)
|
if (propsA===null)
|
||||||
return [...Object.keys(propsB)]
|
return [...Object.keys(propsB)]
|
||||||
const keys = unique([...Object.keys(propsA), ...Object.keys(propsB)])
|
const keys = unique([...Object.keys(propsA), ...Object.keys(propsB)])
|
||||||
return keys.filter((k)=> !(k in propsA) || propsA[k] !== undefined && propsA[k] !== propsB[k])
|
return keys.filter((k)=> propsB[k] !== undefined && (!(k in propsA) || propsA[k] !== propsB[k]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function dirtyItems(a, b) {
|
function dirtyItems(a, b) {
|
||||||
const result = {}
|
const result = {}
|
||||||
for (const k of dirtyKeys(this.tvProps, props))
|
for (const k of dirtyKeys(a,b))
|
||||||
result[k] = props[k]
|
result[k] = b[k]
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,7 +356,7 @@ class ShapeTVCallbacks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onProps(shapeId, _tvShape, props) {
|
onProps(shapeId, _tvShape, props) {
|
||||||
// if (this.shape.debug) console.log('tvOnProps', props)
|
if (this.shape.debug) console.log('tvOnProps', props)
|
||||||
if (this.creating) { // todo still useful?
|
if (this.creating) { // todo still useful?
|
||||||
this.creating = false
|
this.creating = false
|
||||||
return
|
return
|
||||||
@@ -378,9 +440,12 @@ export class HLine extends Line {
|
|||||||
|
|
||||||
|
|
||||||
setModel(model) {
|
setModel(model) {
|
||||||
console.log('hline setModel', model)
|
|
||||||
super.setModel(model)
|
super.setModel(model)
|
||||||
if (model.price !== undefined && model.price !== this.model.price) {
|
if (model.price === null) {
|
||||||
|
this.model.price = null
|
||||||
|
this.delete()
|
||||||
|
}
|
||||||
|
else if (model.price !== this.model.price) {
|
||||||
this.model.price = model.price
|
this.model.price = model.price
|
||||||
this.setPoints([{time:0,price:this.model.price}])
|
this.setPoints([{time:0,price:this.model.price}])
|
||||||
}
|
}
|
||||||
@@ -416,6 +481,7 @@ export class VLine extends Line {
|
|||||||
|
|
||||||
// Model
|
// Model
|
||||||
this.model.time = null
|
this.model.time = null
|
||||||
|
this.debug=true
|
||||||
|
|
||||||
this.setModel(model) // call setModel at the end
|
this.setModel(model) // call setModel at the end
|
||||||
}
|
}
|
||||||
@@ -427,23 +493,23 @@ export class VLine extends Line {
|
|||||||
|
|
||||||
|
|
||||||
setModel(model) {
|
setModel(model) {
|
||||||
if (this.debug) console.log('vline setModel', this.model.time, model )
|
|
||||||
super.setModel(model)
|
super.setModel(model)
|
||||||
if (model.time !== undefined && model.time !== this.model.time) {
|
if (model.time === null) {
|
||||||
|
this.model.time = null
|
||||||
|
this.delete()
|
||||||
|
}
|
||||||
|
else {
|
||||||
this.model.time = model.time
|
this.model.time = model.time
|
||||||
const time = nearestOhlcStart(model.time);
|
const time = nearestOhlcStart(model.time);
|
||||||
if (this.debug) console.log('vline setPoints', this.id, time)
|
|
||||||
this.setPoints([{time, price:0}])
|
this.setPoints([{time, price:0}])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onPoints(points) {
|
onPoints(points) {
|
||||||
if (this.debug) console.log('vline onPoints', this.ourPoints, points)
|
|
||||||
super.onPoints(points);
|
super.onPoints(points);
|
||||||
const orig = this.ourPoints && this.ourPoints.length ? this.ourPoints[0].time : null
|
const orig = this.ourPoints && this.ourPoints.length ? this.ourPoints[0].time : null
|
||||||
const time = points[0].time;
|
const time = points[0].time;
|
||||||
if (!timeAdjustmentTooSmall(orig, time)) {
|
if (!timeAdjustmentTooSmall(orig, time)) {
|
||||||
if (this.debug) console.log('updateModel', time)
|
|
||||||
this.updateModel({time: time})
|
this.updateModel({time: time})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -175,6 +175,7 @@ function getModelValue(model) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setModelValue(model, value) {
|
function setModelValue(model, value) {
|
||||||
|
console.log('setModelValue->', {...model}, value)
|
||||||
if (model.price !== value)
|
if (model.price !== value)
|
||||||
model.price = value
|
model.price = value
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ const color = computed({
|
|||||||
set(v) {
|
set(v) {
|
||||||
const maxLightness = 60
|
const maxLightness = 60
|
||||||
const c = new Color(v).hsl()
|
const c = new Color(v).hsl()
|
||||||
props.builder.color = c.saturation <= maxLightness ? v : c.lightness(maxLightness).string()
|
props.builder.color = c.saturation <= maxLightness ? v : c.lightness(maxLightness).rgb().string()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const colors = computed( ()=> {
|
const colors = computed( ()=> {
|
||||||
@@ -177,7 +177,7 @@ const colorStyle = computed(() => {
|
|||||||
function allocText(weight) {
|
function allocText(weight) {
|
||||||
const alloc = props.builder.allocation
|
const alloc = props.builder.allocation
|
||||||
if (alloc===null) return ''
|
if (alloc===null) return ''
|
||||||
return allocationText(props.order.amount, weight * alloc, amountSymbol.value);
|
return allocationText(weight * alloc, props.order.amount * alloc, amountSymbol.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -214,11 +214,12 @@ function translateOnDrag(shape) {
|
|||||||
|
|
||||||
const shapeA = createShape(valueA.value, {color: defaultColor},
|
const shapeA = createShape(valueA.value, {color: defaultColor},
|
||||||
function (model) {
|
function (model) {
|
||||||
|
console.log('shapeA onModel', model)
|
||||||
const value = props.getModelValue(model);
|
const value = props.getModelValue(model);
|
||||||
if (value!==null && value!==undefined)
|
if (value!==null && value!==undefined)
|
||||||
valueA.value = value;
|
valueA.value = value;
|
||||||
if (model.color)
|
if (model.color)
|
||||||
props.builder.color = model.color;
|
color.value = model.color
|
||||||
},
|
},
|
||||||
deleteSelf)
|
deleteSelf)
|
||||||
|
|
||||||
@@ -231,25 +232,21 @@ const shapeB = createShape(valueB.value, {color:defaultColor},
|
|||||||
if (value!==null && value!==undefined)
|
if (value!==null && value!==undefined)
|
||||||
valueB.value = value;
|
valueB.value = value;
|
||||||
if (model.color)
|
if (model.color)
|
||||||
props.builder.color = model.color;
|
color.value = model.color
|
||||||
},
|
},
|
||||||
deleteSelf)
|
deleteSelf)
|
||||||
|
|
||||||
function interiorOnModel(model) {
|
function interiorOnModel(model) {
|
||||||
const v = model.textColor || model.lineColor || model.color
|
if (model.color)
|
||||||
if (v)
|
color.value = model.color
|
||||||
color.value = v
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let interiorShapes = []
|
let interiorShapes = []
|
||||||
|
|
||||||
function createInteriorShape(price, weight) {
|
function createInteriorShape(index) {
|
||||||
const model = {text: allocText(weight), color: props.builder.color};
|
const shape = new props.shape(makeModel(index), interiorOnModel, deleteSelf)
|
||||||
const shape = createShape(price, model, interiorOnModel, deleteSelf)
|
|
||||||
shape.debug = true
|
|
||||||
translateOnDrag(shape)
|
translateOnDrag(shape)
|
||||||
interiorShapes.push(shape)
|
interiorShapes.push(shape)
|
||||||
// shape.create() // should happen automatically when a model with valid points is set
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -260,6 +257,23 @@ function removeInteriorShape() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function makeModel(index) {
|
||||||
|
const ws = weights.value
|
||||||
|
const w = ws[index]
|
||||||
|
const alloc = props.builder.allocation * ws[index];
|
||||||
|
console.log('makeModel', index, ws, w, alloc)
|
||||||
|
const result = {
|
||||||
|
color: color.value,
|
||||||
|
allocation: alloc,
|
||||||
|
maxAllocation: Math.max(...weights.value),
|
||||||
|
amount: props.order.amount * alloc,
|
||||||
|
amountSymbol: amountSymbol.value,
|
||||||
|
}
|
||||||
|
props.setModelValue(result, values.value[index])
|
||||||
|
console.log('made model', result)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
function adjustShapes() {
|
function adjustShapes() {
|
||||||
// this is where all the shapes are created or adjusted
|
// this is where all the shapes are created or adjusted
|
||||||
console.log('adjustShapes()', valueA.value, valueB.value)
|
console.log('adjustShapes()', valueA.value, valueB.value)
|
||||||
@@ -280,13 +294,7 @@ function adjustShapes() {
|
|||||||
//
|
//
|
||||||
// SINGLE SHAPE
|
// SINGLE SHAPE
|
||||||
//
|
//
|
||||||
if (!shapeA.beingDragged()) {
|
shapeA.setModel(makeModel(0))
|
||||||
const model = {text: allocText(ws[0]), color: colorStrings[0]};
|
|
||||||
console.log('single shape A setModelValue', model, vs[0])
|
|
||||||
props.setModelValue(model, vs[0])
|
|
||||||
console.log('shapeA setModel', model, shapeA.id)
|
|
||||||
shapeA.setModel(model)
|
|
||||||
}
|
|
||||||
shapeB.delete()
|
shapeB.delete()
|
||||||
if (interiorShapes.length) {
|
if (interiorShapes.length) {
|
||||||
for( const shape of interiorShapes )
|
for( const shape of interiorShapes )
|
||||||
@@ -298,19 +306,8 @@ function adjustShapes() {
|
|||||||
//
|
//
|
||||||
// VALUE RANGE
|
// VALUE RANGE
|
||||||
//
|
//
|
||||||
if (!shapeA.beingDragged()) {
|
shapeA.setModel(makeModel(0))
|
||||||
const model = {text: allocText(ws[0]), color: colorStrings[0]};
|
shapeB.setModel(makeModel(vs.length-1))
|
||||||
console.log('shape A not dragged setModelValue', model, vs[0])
|
|
||||||
props.setModelValue(model, vs[0])
|
|
||||||
shapeA.setModel(model)
|
|
||||||
}
|
|
||||||
if (!shapeB.beingDragged()) {
|
|
||||||
const last = colorStrings.length - 1
|
|
||||||
const model = {text: allocText(ws[last]), color: colorStrings[last]};
|
|
||||||
console.log('shape B not dragged setModelValue', model, vs[last])
|
|
||||||
props.setModelValue(model, vs[last])
|
|
||||||
shapeB.setModel(model)
|
|
||||||
}
|
|
||||||
const numInterior = Math.max(0,vs.length-2);
|
const numInterior = Math.max(0,vs.length-2);
|
||||||
// trim excess interior shapes
|
// trim excess interior shapes
|
||||||
while( interiorShapes.length > numInterior )
|
while( interiorShapes.length > numInterior )
|
||||||
@@ -320,13 +317,9 @@ function adjustShapes() {
|
|||||||
const v = vs[i]
|
const v = vs[i]
|
||||||
const w = ws[i];
|
const w = ws[i];
|
||||||
if (i-1 === interiorShapes.length)
|
if (i-1 === interiorShapes.length)
|
||||||
createInteriorShape(v, w)
|
createInteriorShape(i)
|
||||||
else if (!interiorShapes[i-1].beingDragged()) {
|
else
|
||||||
const model = {text: allocText(w), color: colorStrings[i]};
|
interiorShapes[i-1].setModel(makeModel(i))
|
||||||
console.log('interior setModelValue', model, v)
|
|
||||||
props.setModelValue(model, v)
|
|
||||||
interiorShapes[i-1].setModel(model)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ''
|
return ''
|
||||||
|
|||||||
@@ -178,9 +178,9 @@ export function uuid() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function lightenColor(color, lightness = 85, alpha = null) {
|
export function lightenColor(color, lightness = 85, alpha = null) {
|
||||||
let c = new Color(color).hsl().lightness(lightness)
|
let c = new Color(color).hsl().lightness(lightness).rgb()
|
||||||
if (alpha !== null)
|
if (alpha !== null)
|
||||||
c = c.rgb().alpha(alpha)
|
c = c.alpha(alpha)
|
||||||
return c.string()
|
return c.string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -267,10 +267,19 @@ export function weightColors(weights, color) {
|
|||||||
return adj.map((a) => a.string())
|
return adj.map((a) => a.string())
|
||||||
}
|
}
|
||||||
|
|
||||||
export function allocationText(amount, weight, symbol) {
|
export function allocationText(weight, amount, symbol) {
|
||||||
// console.log('weight', weight, alloc, props.amount)
|
let text = ''
|
||||||
const a = amount * weight
|
const hasWeight = weight!==null && weight!==undefined
|
||||||
return `${(weight * 100).toFixed(1)}% = ${a.toLocaleString('fullwide')} ${symbol}`
|
if (hasWeight)
|
||||||
|
text += `${(weight * 100).toFixed(1)}%`
|
||||||
|
const hasAmount = amount!==null && amount!==undefined
|
||||||
|
const hasSymbol = symbol!==null && symbol!==undefined
|
||||||
|
if (hasAmount && hasSymbol) {
|
||||||
|
if (hasWeight)
|
||||||
|
text += ' = '
|
||||||
|
text += `${amount.toLocaleString('fullwide')} ${symbol}`
|
||||||
|
}
|
||||||
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteBuilder(order, builder) {
|
export function deleteBuilder(order, builder) {
|
||||||
|
|||||||
Reference in New Issue
Block a user