Created
June 20, 2020 01:43
-
-
Save AlexanderBollbach/7ccf81c17465caa70a8c751e34853b25 to your computer and use it in GitHub Desktop.
a crazy over the top graph i made to model project types in my drawing app
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//import UIKit | |
//import ComposableArchitecture | |
// | |
//enum RelationType: String, Hashable, Codable { | |
// case selected | |
// case child | |
// case soloed | |
//} | |
// | |
//protocol GraphType { | |
// var graphType: String { get } | |
//} | |
// | |
//struct Node: Hashable, Codable { | |
// let id: UUID | |
// var value: NodeValue | |
//} | |
// | |
//extension Node: GraphType { | |
// var graphType: String { | |
// value.graphType | |
// } | |
//} | |
// | |
//struct Relation: Hashable, Codable { | |
// | |
// let from: UUID | |
// let to: UUID | |
// | |
// let type: RelationType | |
// | |
// var weight: Int | |
// | |
// init(from: UUID, to: UUID, type: RelationType, weight: Int = 1) { | |
// self.from = from | |
// self.to = to | |
// self.type = type | |
// self.weight = weight | |
// } | |
//} | |
// | |
//struct Graph: Equatable, Codable { | |
// private var nodes: Set<Node> = [] | |
// private var relations: Set<Relation> = [] | |
//} | |
// | |
// | |
//enum RelationSpec { | |
// case fromIds([UUID]) | |
// case toIds([UUID]) | |
// case relationType(RelationType) | |
// case weight(Int) | |
// case exact(Relation) | |
//} | |
// | |
//extension Graph { | |
// | |
// func match(relation: Relation, specs: [RelationSpec]) -> Bool { | |
// | |
// func matchFromIds(_ ids: [UUID]) -> Bool { | |
// ids.contains(relation.from) | |
// } | |
// func matchToIds(_ ids: [UUID]) -> Bool { | |
// ids.contains(relation.to) | |
// } | |
// func matchRelationType(_ value: RelationType) -> Bool { | |
// relation.type == value | |
// } | |
// func matchWeight(value: Int) -> Bool { | |
// relation.weight == value | |
// } | |
// func matchExact(value: Relation) -> Bool { | |
// relation == value | |
// } | |
// | |
// for spec in specs { | |
// switch spec { | |
// case let .fromIds(ids): | |
// if !matchFromIds(ids) { | |
// return false | |
// } | |
// case let .toIds(ids): | |
// if !matchToIds(ids) { | |
// return false | |
// } | |
// case let .relationType(value): | |
// if !matchRelationType(value) { | |
// return false | |
// } | |
// case let .weight(value): | |
// if !matchWeight(value: value) { | |
// return false | |
// } | |
// case let .exact(value): | |
// if !matchExact(value: value) { | |
// return false | |
// } | |
// } | |
// } | |
// | |
// return true | |
// } | |
//} | |
// | |
//extension Graph { | |
// | |
// mutating func addNode(id: UUID, value: NodeValue) { | |
// nodes.insert(.init(id: id, value: value)) | |
// } | |
// | |
// mutating func updateNode<T>( | |
// id: UUID, | |
// casePath: CasePath<NodeValue, T>, | |
// transform: (inout T) -> Void | |
// ) { | |
// | |
// let newNodes: [Node] = nodes.map { | |
// var s = $0 | |
// if s.id == id { | |
// if var val = casePath.extract(from: s.value) { | |
// transform(&val) | |
// s.value = casePath.embed(val) | |
// } | |
// } | |
// return s | |
// } | |
// | |
// self.nodes = Set(newNodes) | |
// } | |
// | |
// func endNodes(relations: [Relation]) -> [Node] { | |
// let endIds = relations.map { $0.to } | |
// return endIds.map { endId in nodes.first(where: { $0.id == endId })! } | |
// } | |
// | |
// mutating func purgeNodes(ids: [UUID]) { | |
// | |
// // remove nodes themselves | |
// removeNodes(ids: ids) | |
// | |
// // remove all edges to them | |
// removeRelations( | |
// matching: [ | |
// .toIds(ids) | |
// ] | |
// ) | |
// } | |
// | |
// func endNodeValues<T>( | |
// relations: [Relation], | |
// casePath: CasePath<NodeValue, T> | |
// ) -> [(UUID, T)] { | |
// | |
// relations.map { $0.to } | |
// .map { endId in nodes.first(where: { $0.id == endId })! } | |
// .map { | |
// guard let value = casePath.extract(from: $0.value) else { fatalError() } | |
// return ($0.id, value) | |
// } | |
// } | |
// | |
// func allNodes(ids: [UUID]) -> [Node] { | |
// nodes.filter { ids.contains($0.id) } | |
// } | |
// | |
// mutating func removeNodes(ids: [UUID]) { | |
// nodes = nodes.filter { !ids.contains($0.id) } | |
// } | |
// | |
// mutating func removeNodes(_ nodesToRemove: [Node]) { | |
// let ids = nodesToRemove.map { $0.id } | |
// nodes = nodes.filter { !ids.contains($0.id) } | |
// } | |
// | |
// mutating func removeNode(at id: UUID) { | |
// nodes = nodes.filter { $0.id != id } | |
// } | |
// | |
// func ids(type: String) -> [UUID] { | |
// allNodes(graphType: type).map { $0.id } | |
// } | |
// | |
// func allNodes(graphType: String) -> [Node] { | |
// nodes | |
// .filter { $0.graphType == graphType } | |
// } | |
// | |
// mutating func add(_ relation: Relation) { | |
// relations.insert(relation) | |
// } | |
// | |
// mutating func add(_ relations: Set<Relation>) { | |
// self.relations = self.relations.intersection(relations) | |
// } | |
// | |
// mutating func addNode(value: NodeValue) { | |
// nodes.insert(.init(id: UUID(), value: value)) | |
// } | |
// | |
// mutating func add(node: Node) { | |
// nodes.insert(node) | |
// } | |
// | |
// func firstRelation(matching specs: [RelationSpec]) -> Relation? { | |
// relations.first { | |
// self.match(relation: $0, specs: specs) | |
// } | |
// } | |
// | |
// func allRelations( | |
// matching specs: [RelationSpec], | |
// orderedByWeight: Bool = true | |
// ) -> [Relation] { | |
// | |
// let rs = relations.filter { | |
// self.match(relation: $0, specs: specs) | |
// } | |
// | |
// if orderedByWeight { | |
// return Array(rs.sorted(by: { $0.weight < $1.weight })) | |
// } else { | |
// return Array(rs) | |
// } | |
// } | |
// | |
// mutating func removeRelations(matching specs: [RelationSpec]) { | |
// relations = relations.filter { | |
// !self.match(relation: $0, specs: specs) | |
// } | |
// } | |
// | |
// mutating func updateRelation(matching specs: [RelationSpec], transform: (inout Relation) -> Void) { | |
// | |
// let rs = relations.map { r -> Relation in | |
// var r = r | |
// if self.match(relation: r, specs: specs) { | |
// transform(&r) | |
// } | |
// return r | |
// } | |
// | |
// relations = Set(rs) | |
// } | |
// | |
// mutating func duplicateNode(id: UUID) { | |
// | |
// } | |
// } | |
// | |
//// logging | |
// | |
//extension Graph { | |
// | |
// func show() { | |
// nodes.forEach { self.show(node: $0) } | |
// relations.forEach { self.show(relation: $0) } | |
// } | |
// | |
// func show(node id: UUID) { | |
// guard let node = (nodes.first { $0.id == id }) else { fatalError() } | |
// show(node: node) | |
// } | |
// | |
// func show(node: Node) { | |
// print("--node--") | |
// print(node.id) | |
// print(node.value) | |
// print(" ") | |
// } | |
// | |
// func show(relation: Relation) { | |
// print("--relation--") | |
// print("type: \(relation.type)") | |
// | |
// show(node: relation.from) | |
// print("-------- to --------") | |
// show(node: relation.to) | |
// | |
// print(" ") | |
// } | |
//} | |
/// HERE IS SOME EXAMPLE USAGE | |
// | |
//import Foundation | |
// | |
// | |
//extension Project { | |
// | |
// mutating func swapLayerRight(id: UUID) { | |
// | |
// if let r1 = graph.firstRelation(matching: [.toIds([id]), .relationType(.child)]) { | |
// if let r2 = graph.firstRelation(matching: [.relationType(.child), .weight(r1.weight + 1)]) { | |
// | |
// graph.updateRelation(matching: [.exact(r1)]) { relation in | |
// relation.weight = r2.weight | |
// } | |
// | |
// graph.updateRelation(matching: [.exact(r2)]) { relation in | |
// relation.weight = r1.weight | |
// } | |
// } | |
// } | |
// } | |
// | |
// mutating func swapLayerLeft(id: UUID) { | |
// | |
// if let r1 = graph.firstRelation(matching: [.toIds([id]), .relationType(.child)]) { | |
// if let r2 = graph.firstRelation(matching: [.relationType(.child), .weight(r1.weight - 1)]) { | |
// | |
// graph.updateRelation(matching: [.exact(r1)]) { relation in | |
// relation.weight = r2.weight | |
// } | |
// | |
// graph.updateRelation(matching: [.exact(r2)]) { relation in | |
// relation.weight = r1.weight | |
// } | |
// } | |
// } | |
// } | |
// | |
// func isSoloed(layerId: UUID) -> Bool { | |
// | |
// let r = graph.firstRelation( | |
// matching: [ | |
// .fromIds([projectInfo.id]), | |
// .toIds(graph.ids(type: "Layer")), | |
// .relationType(.soloed) | |
// ] | |
// ) | |
// | |
// return r != nil | |
// } | |
// | |
// var hasSoloedLayers: Bool { | |
// | |
// let rs = graph.allRelations( | |
// matching: [ | |
// .fromIds([projectInfo.id]), | |
// .toIds(graph.ids(type: "Layer")), | |
// .relationType(.soloed) | |
// ] | |
// ) | |
// | |
// return !rs.isEmpty | |
// } | |
// | |
// mutating func unSoloLayers() { | |
// graph.removeRelations( | |
// matching: [ | |
// .fromIds([projectInfo.id]), | |
// .toIds(graph.ids(type: "Layer")), | |
// .relationType(.soloed) | |
// ] | |
// ) | |
// } | |
// | |
// mutating func removeSelectedLayers() { | |
// graph.purgeNodes(ids: selectedLayerIds) | |
// } | |
// | |
// mutating func toggleLayersSolo(ids: [UUID]) { | |
// | |
// for id in ids { | |
// | |
// let specs: [RelationSpec] = [ | |
// .fromIds([projectInfo.id]), | |
// .toIds(graph.ids(type: "Layer")), | |
// .relationType(.soloed) | |
// ] | |
// | |
// if let _ = graph.firstRelation(matching: specs) { | |
// graph.removeRelations(matching: specs) | |
// } else { | |
// graph.add(Relation(from: projectInfo.id, to: id, type: .selected)) | |
// } | |
// } | |
// } | |
// | |
// mutating func selectLayerOnly(id: UUID) { | |
// | |
// graph.removeRelations( | |
// matching: [ | |
// .fromIds(graph.ids(type: "ProjectInfo")), | |
// .toIds(graph.ids(type: "Layer")), | |
// .relationType(.selected) | |
// ] | |
// ) | |
// | |
// graph.add(Relation(from: projectInfo.id, to: id, type: .selected)) | |
// } | |
// | |
// mutating func toggleLayersSelection(ids: [UUID]) { | |
// | |
// for id in ids { | |
// | |
// let specs: [RelationSpec] = [ | |
// .fromIds([projectInfo.id]), | |
// .toIds([id]), | |
// .relationType(.selected) | |
// ] | |
// | |
// if let _ = graph.firstRelation(matching: specs) { | |
// graph.removeRelations(matching: specs) | |
// } else { | |
// graph.add(Relation(from: projectInfo.id, to: id, type: .selected)) | |
// } | |
// } | |
// } | |
// | |
// var layers: [IdentifiedLayer] { | |
// | |
// let rs = graph.allRelations( | |
// matching: [ | |
// .fromIds([projectInfo.id]), | |
// .toIds(graph.ids(type: "Layer")), | |
// .relationType(.child) | |
// ] | |
// ) | |
// | |
// return graph | |
// .endNodes(relations: rs) | |
// .map { | |
// if case let NodeValue.layer(value) = $0.value { | |
// return .init(id: $0.id, value: value) | |
// } else { | |
// fatalError() | |
// } | |
// } | |
// } | |
// | |
// var selectedLayerIds: [UUID] { | |
// | |
// let relations = graph.allRelations( | |
// matching: [ | |
// .fromIds([projectInfo.id]), | |
// .toIds(graph.ids(type: "Layer")), | |
// .relationType(.selected) | |
// ] | |
// ) | |
// | |
// return relations.map { $0.to } | |
// } | |
// | |
// var soloedLayerIds: [UUID] { | |
// | |
// let relations = graph.allRelations( | |
// matching: [ | |
// .fromIds([projectInfo.id]), | |
// .toIds(graph.ids(type: "Layer")), | |
// .relationType(.soloed) | |
// ] | |
// ) | |
// | |
// return relations.map { $0.to } | |
// } | |
// | |
// mutating func createAndSelectNewLayer() { | |
// | |
// let title = "Layer \(graph.allNodes(graphType: "Layer").count)" | |
// | |
// let layerId = UUID() | |
// graph.add(node: .init(id: layerId, value: .layer(.init(title: title)))) | |
// | |
// let highestWeightedRelation = graph.allRelations( | |
// matching: [ | |
// .fromIds([projectInfo.id]), | |
// .toIds(graph.ids(type: "Layer")), | |
// .relationType(.child) | |
// ] | |
// ).max(by: { $1.weight > $0.weight }) | |
// | |
// let nextWeight = (highestWeightedRelation?.weight ?? 0) + 1 | |
// | |
// // add child relation | |
// graph.add( | |
// Relation( | |
// from: projectInfo.id, | |
// to: layerId, | |
// type: .child, | |
// weight: nextWeight | |
// ) | |
// ) | |
// | |
// // remove all selected relations | |
// graph.removeRelations( | |
// matching: [ | |
// .fromIds([projectInfo.id]), | |
// .relationType(.selected) | |
// ] | |
// ) | |
// | |
// // add selected relation | |
// graph.add( | |
// Relation( | |
// from: projectInfo.id, | |
// to: layerId, | |
// type: .selected | |
// ) | |
// ) | |
// } | |
// | |
// mutating func soloSelectedLayers() { | |
// for layer in selectedLayerIds { | |
// graph.removeRelations( | |
// matching: [ | |
// .fromIds([projectInfo.id]), | |
// .toIds(graph.ids(type: "Layer")), | |
// .relationType(.soloed) | |
// ] | |
// ) | |
// | |
// graph.add(.init(from: projectInfo.id, to: layer, type: .soloed, weight: 0)) | |
// } | |
// } | |
// | |
// mutating func unsoloAllLayers() { | |
// graph.removeRelations( | |
// matching: [ | |
// .fromIds([projectInfo.id]), | |
// .toIds(graph.ids(type: "Layer")), | |
// .relationType(.soloed) | |
// ] | |
// ) | |
// } | |
// | |
// mutating func duplicateLayer(id: UUID) { | |
// | |
// let oldLayerId = id | |
// | |
// func nodeIds(fromId: UUID, childType: String) -> [UUID] { | |
// let rs = graph.allRelations( | |
// matching: [ | |
// .fromIds([fromId]), | |
// .toIds(graph.ids(type: childType)), | |
// .relationType(.child) | |
// ] | |
// ) | |
// | |
// return graph.endNodes(relations: rs).map { $0.id } | |
// } | |
// | |
// guard let projectInfoId = graph.ids(type: "ProjectInfo").first else { fatalError() } | |
// guard let node = graph.allNodes(ids: [id]).first, case let NodeValue.layer(oldLayer) = node.value else { fatalError() } | |
// | |
// let newLayerId = UUID() | |
// | |
// graph.add(node: Node(id: newLayerId, value: .layer(oldLayer))) | |
// graph.add(Relation(from: projectInfoId, to: newLayerId, type: .child, weight: self.layers.count + 1)) | |
// | |
// let effectIds = nodeIds(fromId: oldLayerId, childType: "Effect") | |
// | |
// for (offset, effectId) in effectIds.enumerated() { | |
// guard let node = graph.allNodes(ids: [effectId]).first, case let NodeValue.effect(oldEffect) = node.value else { fatalError() } | |
// | |
// let attributeIds = nodeIds(fromId: effectId, childType: "Attribute") | |
// | |
// let newEffectId = UUID() | |
// | |
// graph.add(node: Node(id: newEffectId, value: .effect(oldEffect))) | |
// graph.add(.init(from: newLayerId, to: newEffectId, type: .child, weight: offset)) | |
// | |
// for attributeId in attributeIds.enumerated() { | |
// | |
// guard let node = graph.allNodes(ids: [attributeId.element]).first, case let NodeValue.attribute(oldAttribute) = node.value else { fatalError() } | |
// | |
// let newAttributeId = UUID() | |
// | |
// graph.add(node: Node(id: newAttributeId, value: .attribute(oldAttribute))) | |
// graph.add(Relation(from: newEffectId, to: newAttributeId, type: .child, weight: attributeId.offset)) | |
// } | |
// } | |
// } | |
//} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment