-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New: Add state setters with options #95
New: Add state setters with options #95
Conversation
if (data.type === 'node' && data.options.isSingle) { | ||
const nodes = this._nodes.getAll(); | ||
|
||
for (let i = 0; i < nodes.length; i++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like these iterations through all nodes/edges can be slow. Any ideas? And do we want to clear the state of all nodes/edges or only the adjusting ones?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about using hash maps? Obviously, this would require a lot of code change. Just starting a discussion, I don't think it's doable right now.
For the second question, I would say only the ones that are being adjusted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or maybe to supplement this nodes array we can keep an index where nodeIndex[node.id] === nodes.findIndex((el) => el.id === node.id)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you suggesting storing nodes in a hashmap? As far as I know, retrieving elements from an array by a known index is O(1)
complexity, so iterating through an array vs a hashmap (which also has O(1)
complexity for retrieving element by key) may not make a big difference here. I might have misunderstood your point, or perhaps I missed something, so feel free to clarify it further :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If all nodes are in state X, and you want to clear them up if they are not node with ID 1, you still need to to N - 1
updates. So hash maps or any other structure won't help there, you already have the fastest structure, an array.
But, you can do this and increase the memory footprint: I am not sure if it makes sense to go into this right now, but this would be the plan:
- Graph keeps a structure of nodes/edges by state, so each state has a structure of nodes and edges (probably just id) - a set of a map.
- States in nodes and edges are just views, readonly
- When state is updated in node, edge it is just updated in the graph
This way, if you have isSingle: true
, graph, as an owner of the data can then literally take all selected nodes from a set or a map, clear those states and set the new node as single selected. This would improve the speed, but it would worse the memory footprint and code simplitiy because owner of the state becomes the graph, not graph objects (node/edge).
I would keep this as is and fix only if we see that it hurts performance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I misunderstood the functionality. I saw ===
instead of !==
. I thought you were trying to clear the state of anode with a specific id
.
src/models/edge.ts
Outdated
if (options && options.isToggle) { | ||
this._toggleState(result.state); | ||
} else { | ||
this._state = result.state; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either have this._state = <function that handles options in general>(results.state)
where function doesn't mutate the state, but returns what the new state should be, or keep this as is, but still change the toggleState
to not mutate the this._state
because I don't see a reason why it should. The best functions are the clean ones, which do not mutate state (if it is possible to make them).
src/models/edge.ts
Outdated
if (typeof result === 'number') { | ||
this._state = result; | ||
} else if (typeof result === 'object') { | ||
const options = result.options; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For these checks, please use utils/type.utils.ts
because of:
- cleaner code,
isNumber
vs `typeof x === 'number' - handles edge cases,
isPlainObject
vstypeof result === 'object'
(edge case is:typeof null
will return'object'
).
src/models/node.ts
Outdated
result = arg; | ||
} | ||
|
||
if (typeof result === 'number') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comments as for edges.
src/models/state.ts
Outdated
export interface IGraphObjectStateOptions { | ||
isToggle?: boolean; | ||
isSingle?: boolean; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In these cases, when it is options, it would be best not to have optionals here, but where you use it, line 17: Partial<IGraphObjectStateOptions>
where options can also be optional, e.g. options?: Partial<...>
.
If you move external logic (having optional params) too deep into the type, it is harder to handle that in some other cases. E.g. if you wish to create default params, you could then just say: const default: IGraphObjectStateOptions = { ... }
and you would be forced to define both. If you would use options
from Parameters
you would see that this object is optional here with all its children. The context of when is something optional, when it isn't is being kept.
if (data.type === 'node' && data.options.isSingle) { | ||
const nodes = this._nodes.getAll(); | ||
|
||
for (let i = 0; i < nodes.length; i++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If all nodes are in state X, and you want to clear them up if they are not node with ID 1, you still need to to N - 1
updates. So hash maps or any other structure won't help there, you already have the fastest structure, an array.
But, you can do this and increase the memory footprint: I am not sure if it makes sense to go into this right now, but this would be the plan:
- Graph keeps a structure of nodes/edges by state, so each state has a structure of nodes and edges (probably just id) - a set of a map.
- States in nodes and edges are just views, readonly
- When state is updated in node, edge it is just updated in the graph
This way, if you have isSingle: true
, graph, as an owner of the data can then literally take all selected nodes from a set or a map, clear those states and set the new node as single selected. This would improve the speed, but it would worse the memory footprint and code simplitiy because owner of the state becomes the graph, not graph objects (node/edge).
I would keep this as is and fix only if we see that it hurts performance.
if (data && 'type' in data && 'options' in data && 'isSingle' in data.options) { | ||
if (data.type === 'node' && data.options.isSingle) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you maybe simplify this to if (data && data.options?.isSingle && data.type === 'node')
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe it could be possible, but only after reworking IObserverDataPayload
into a more generic solution as you mentioned here. Currently, I am using some sort of custom type guard because of the interfaces used in that payload
this.notifyListeners(); | ||
} | ||
|
||
private _handleState(state: number, options?: Partial<IGraphObjectStateOptions>): number { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is nitpicking really, but maybe getNextState
would be a better name.
* New: Add node properties setters * New: Add edge properties setters * New: Add properties getters * New: Add patch for nodes and edges * Fix: Make getters return copies * Fix: Edge factory listeners copying * Fix: Jest outdated tests * Fix: Github actions node version * Chore: Refactor observer interface * Chore: Refactor node/edge constructor settings * Chore: Refactor node/edge function grouping * Chore: Refactor node/edge function grouping * Fix: Listeners behaviour * Chore: Refactor property copying * Chore: Refactor subject implementation * Fix: Set position behaviour on node drag * Chore: Upgrade node version * Chore: Refactor function type check * Fix: Remove listener behaviour * Chore: Refactor util naming * Chore: Remove unused type assertion * Chore: Refactor position setter options * Chore: Refactor property patch function * Fix: Set map node position behaviour * Chore: Refactor simulator data patching * Chore: Change observers to callbacks * New: Add state setters with options (#95) * New: Add state setters with options * Chore: Remove leftover comments * Chore: Refactor state setter logic * Chore: Refactor state types * Fix: Rename merged function usage * Fix: Merged variable naming * Chore: Fix tests
* New: Add new simulator (#56) * New: Add simulator scenarios for manual testing * New: Refactor simulator (WIP) * New: Add progress overlay, Update descriptions * Fix: Introduce new simulator event, Fix main-thread behavior * Fix: Rearrange class methods based on visibility * Fix: Improve naming * Chore(release): 0.2.0 * Fix: Tweak simulator, adjust API slightly * Fix: Temporarily patch some physics behavior * Fix: Adjust re-heating parameters * Fix: tweak physics behavior -> immediately stop sim when disabling * Chore: Remove the beta from release branches --------- Co-authored-by: dlozic <davidlozic@gmail.com> * New: Add zoom and recenter functions (#74) * New: Add zoom and recenter functions * Fix: Reduce excessive recentering (#75) * Fix: Remove excessive recenterings * Fix: Remove unused code * Fix: New simulator (#92) * Chore: Refactor naming * New: Add new events * Chore: Refactor code styling * Docs: Remove unused flags * Chore: Remove unused simulation functions * Chore: Refactor view render function calls * Chore: Add missing tests * New: Add removal functions (#96) * New: Add removal functions * Fix: Add missing callback data * Chore: Refactor remove return values * Chore: Refactor remove function type usage * Fix: Default settings for node placement (#98) * New: Add properties setters and getters (#93) * New: Add node properties setters * New: Add edge properties setters * New: Add properties getters * New: Add patch for nodes and edges * Fix: Make getters return copies * Fix: Edge factory listeners copying * Fix: Jest outdated tests * Fix: Github actions node version * Chore: Refactor observer interface * Chore: Refactor node/edge constructor settings * Chore: Refactor node/edge function grouping * Chore: Refactor node/edge function grouping * Fix: Listeners behaviour * Chore: Refactor property copying * Chore: Refactor subject implementation * Fix: Set position behaviour on node drag * Chore: Upgrade node version * Chore: Refactor function type check * Fix: Remove listener behaviour * Chore: Refactor util naming * Chore: Remove unused type assertion * Chore: Refactor position setter options * Chore: Refactor property patch function * Fix: Set map node position behaviour * Chore: Refactor simulator data patching * Chore: Change observers to callbacks * New: Add state setters with options (#95) * New: Add state setters with options * Chore: Remove leftover comments * Chore: Refactor state setter logic * Chore: Refactor state types * Fix: Rename merged function usage * Fix: Merged variable naming * Chore: Fix tests --------- Co-authored-by: dlozic <davidlozic@gmail.com> Co-authored-by: Oleksandr Ichenskyi <55350107+AlexIchenskiy@users.noreply.github.com> Co-authored-by: AlexIchenskiy <aichenskiy@gmail.com>
Resolves #68
As an enhancement to the freshly implemented getters and setters, this PR introduces a new way to set the state of nodes and edges using objects that specify state with additional parameters such as
isSingle
andisToggle
. Now, it is possible to use both the regular setter:and the new setter:
and so on. This provides more customization flexibility to the user, with default values for these parameters set to
false
. It should be easily extendable for adding even more options.