aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuben Beltran del Rio <git@r.bdr.sh>2025-07-07 21:52:16 +0200
committerRuben Beltran del Rio <git@r.bdr.sh>2025-07-07 21:52:16 +0200
commit129bca332b68bd6106079f58bd0de77baf21ec68 (patch)
treee77fc44ca4a5a9a0cb9b653859d7546154472911
parent9cbc1f2bf737e862a01c6859029c3683d6067def (diff)
Take Notes into account in smart labels
-rw-r--r--Map/Business/SmartLabelPositioning/SmartLabelPositioning.swift376
-rw-r--r--Map/Localizable.xcstrings625
-rw-r--r--Map/MapApp.swift67
-rw-r--r--Map/Presentation/Base Components/MapRender/MapMask.swift9
-rw-r--r--Map/Presentation/Base Components/MapRender/MapVertices.swift9
-rw-r--r--Map/Presentation/Complex Components/MapRender/MapRenderView.swift77
6 files changed, 843 insertions, 320 deletions
diff --git a/Map/Business/SmartLabelPositioning/SmartLabelPositioning.swift b/Map/Business/SmartLabelPositioning/SmartLabelPositioning.swift
new file mode 100644
index 0000000..f765108
--- /dev/null
+++ b/Map/Business/SmartLabelPositioning/SmartLabelPositioning.swift
@@ -0,0 +1,376 @@
+// Copyright (C) 2024 Rubén Beltrán del Río
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+import AppKit
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see https://map.tranquil.systems.
+import CoreGraphics
+
+struct SmartLabelPositioning {
+
+ static func calculateOptimalLabelPosition(
+ for vertex: Vertex,
+ mapSize: CGSize,
+ vertexSize: CGSize,
+ edges: [MapEdge],
+ opportunities: [Opportunity],
+ inertias: [Inertia],
+ axisLines: [Line],
+ allVertices: [Vertex] = [],
+ notes: [Note] = []
+ ) -> CGPoint {
+ let vertexPixelPos = CGPoint(
+ x: w(vertex.position.x, mapSize: mapSize),
+ y: h(vertex.position.y, mapSize: mapSize)
+ )
+
+ let actualLabelSize = calculateLabelSize(for: vertex.label)
+
+ let candidatePositions = generateCandidatePositions(
+ vertexPixelPos: vertexPixelPos,
+ vertexSize: vertexSize,
+ labelSize: actualLabelSize
+ )
+
+ let allLines = collectAllLines(
+ edges: edges,
+ opportunities: opportunities,
+ inertias: inertias,
+ axisLines: axisLines,
+ mapSize: mapSize,
+ vertexSize: vertexSize
+ )
+
+ let noteRects = collectNoteRects(
+ notes: notes,
+ mapSize: mapSize
+ )
+
+ let defaultPosition = candidatePositions[0]
+
+ let defaultRect = CGRect(origin: defaultPosition, size: actualLabelSize)
+ if countCollisions(labelRect: defaultRect, lines: allLines) == 0
+ && countNoteCollisions(labelRect: defaultRect, noteRects: noteRects) == 0
+ {
+ return defaultPosition
+ }
+
+ var bestPosition = defaultPosition
+ var bestScore = Double.infinity
+
+ for (index, position) in candidatePositions.enumerated() {
+ let labelRect = CGRect(
+ origin: position,
+ size: actualLabelSize
+ )
+
+ let collisionCount = countCollisions(labelRect: labelRect, lines: allLines)
+ let noteCollisionCount = countNoteCollisions(labelRect: labelRect, noteRects: noteRects)
+ let distance = sqrt(
+ pow(position.x - (vertexPixelPos.x + vertexSize.width / 2), 2)
+ + pow(position.y - (vertexPixelPos.y + vertexSize.height / 2), 2))
+
+ let ownershipPenalty = calculateOwnershipPenalty(
+ labelRect: labelRect,
+ ownVertex: vertex,
+ allVertices: allVertices,
+ mapSize: mapSize,
+ vertexSize: vertexSize
+ )
+
+ let score =
+ Double(collisionCount * 100) + Double(noteCollisionCount * 500) + distance * 0.5 + Double(
+ index) * 0.1 + ownershipPenalty
+
+ if score < bestScore {
+ bestScore = score
+ bestPosition = position
+ }
+ }
+
+ return bestPosition
+ }
+
+ private static func calculateLabelSize(for text: String) -> CGSize {
+ let cleanText = text.replacingOccurrences(of: "\\n", with: "\n")
+ let font = NSFont.systemFont(ofSize: 11)
+ let attributes: [NSAttributedString.Key: Any] = [.font: font]
+ let size = cleanText.boundingRect(
+ with: CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude),
+ options: [.usesLineFragmentOrigin, .usesFontLeading],
+ attributes: attributes,
+ context: nil
+ ).size
+
+ return CGSize(width: ceil(size.width) + 4, height: ceil(size.height) + 2)
+ }
+
+ private static func calculateOwnershipPenalty(
+ labelRect: CGRect,
+ ownVertex: Vertex,
+ allVertices: [Vertex],
+ mapSize: CGSize,
+ vertexSize: CGSize
+ ) -> Double {
+ guard !allVertices.isEmpty else { return 0 }
+
+ let labelCenter = CGPoint(
+ x: labelRect.midX,
+ y: labelRect.midY
+ )
+
+ let ownVertexPixelPos = CGPoint(
+ x: w(ownVertex.position.x, mapSize: mapSize),
+ y: h(ownVertex.position.y, mapSize: mapSize)
+ )
+ let ownVertexCenter = CGPoint(
+ x: ownVertexPixelPos.x + vertexSize.width / 2,
+ y: ownVertexPixelPos.y + vertexSize.height / 2
+ )
+
+ let distanceToOwnVertex = sqrt(
+ pow(labelCenter.x - ownVertexCenter.x, 2) + pow(labelCenter.y - ownVertexCenter.y, 2)
+ )
+
+ for otherVertex in allVertices {
+ if otherVertex.id == ownVertex.id { continue }
+
+ let otherVertexPixelPos = CGPoint(
+ x: w(otherVertex.position.x, mapSize: mapSize),
+ y: h(otherVertex.position.y, mapSize: mapSize)
+ )
+ let otherVertexCenter = CGPoint(
+ x: otherVertexPixelPos.x + vertexSize.width / 2,
+ y: otherVertexPixelPos.y + vertexSize.height / 2
+ )
+
+ let distanceToOtherVertex = sqrt(
+ pow(labelCenter.x - otherVertexCenter.x, 2) + pow(labelCenter.y - otherVertexCenter.y, 2)
+ )
+
+ if distanceToOtherVertex < distanceToOwnVertex {
+ return 50.0
+ }
+ }
+
+ return 0
+ }
+
+ private static func generateCandidatePositions(
+ vertexPixelPos: CGPoint,
+ vertexSize: CGSize,
+ labelSize: CGSize
+ ) -> [CGPoint] {
+ let padding = CGFloat(5.0)
+
+ return [
+ CGPoint(
+ x: vertexPixelPos.x + vertexSize.width + padding,
+ y: vertexPixelPos.y + (vertexSize.height - labelSize.height) / 2
+ ),
+ CGPoint(
+ x: vertexPixelPos.x - labelSize.width - padding,
+ y: vertexPixelPos.y + (vertexSize.height - labelSize.height) / 2
+ ),
+ CGPoint(
+ x: vertexPixelPos.x + (vertexSize.width - labelSize.width) / 2,
+ y: vertexPixelPos.y - labelSize.height - padding
+ ),
+ CGPoint(
+ x: vertexPixelPos.x + (vertexSize.width - labelSize.width) / 2,
+ y: vertexPixelPos.y + vertexSize.height + padding
+ ),
+ CGPoint(
+ x: vertexPixelPos.x + vertexSize.width + padding,
+ y: vertexPixelPos.y - labelSize.height - padding
+ ),
+ CGPoint(
+ x: vertexPixelPos.x - labelSize.width - padding,
+ y: vertexPixelPos.y - labelSize.height - padding
+ ),
+ CGPoint(
+ x: vertexPixelPos.x + vertexSize.width + padding,
+ y: vertexPixelPos.y + vertexSize.height + padding
+ ),
+ CGPoint(
+ x: vertexPixelPos.x - labelSize.width - padding,
+ y: vertexPixelPos.y + vertexSize.height + padding
+ ),
+ ]
+ }
+
+ private static func collectAllLines(
+ edges: [MapEdge],
+ opportunities: [Opportunity],
+ inertias: [Inertia],
+ axisLines: [Line],
+ mapSize: CGSize,
+ vertexSize: CGSize
+ ) -> [Line] {
+ var lines: [Line] = []
+
+ for edge in edges {
+ let origin = CGPoint(
+ x: w(edge.origin.x, mapSize: mapSize), y: h(edge.origin.y, mapSize: mapSize))
+ let destination = CGPoint(
+ x: w(edge.destination.x, mapSize: mapSize), y: h(edge.destination.y, mapSize: mapSize))
+
+ let slope = (destination.y - origin.y) / (destination.x - origin.x)
+ let angle = atan(slope)
+ let multiplier = CGFloat(slope < 0 ? -1.0 : 1.0)
+
+ let offsetOrigin = CGPoint(
+ x: origin.x + multiplier * (vertexSize.width / 2.0) * cos(angle),
+ y: origin.y + multiplier * (vertexSize.height / 2.0) * sin(angle)
+ )
+ let offsetDestination = CGPoint(
+ x: destination.x - multiplier * (vertexSize.width / 2.0) * cos(angle),
+ y: destination.y - multiplier * (vertexSize.height / 2.0) * sin(angle)
+ )
+
+ let adjustedOrigin = CGPoint(
+ x: offsetOrigin.x + vertexSize.width / 2.0,
+ y: offsetOrigin.y + vertexSize.height / 2.0
+ )
+ let adjustedDestination = CGPoint(
+ x: offsetDestination.x + vertexSize.width / 2.0,
+ y: offsetDestination.y + vertexSize.height / 2.0
+ )
+
+ lines.append(Line(start: adjustedOrigin, end: adjustedDestination))
+ }
+
+ for opportunity in opportunities {
+ let origin = CGPoint(
+ x: w(opportunity.origin.x, mapSize: mapSize), y: h(opportunity.origin.y, mapSize: mapSize))
+ let destination = CGPoint(
+ x: w(opportunity.destination.x, mapSize: mapSize),
+ y: h(opportunity.destination.y, mapSize: mapSize))
+
+ let multiplier = CGFloat(opportunity.destination.x > opportunity.origin.x ? 1.0 : -1.0)
+
+ let offsetOrigin = CGPoint(
+ x: origin.x + multiplier * (vertexSize.width / 2.0),
+ y: origin.y
+ )
+ let offsetDestination = CGPoint(
+ x: destination.x - multiplier * (vertexSize.width / 2.0),
+ y: destination.y
+ )
+
+ let adjustedOrigin = CGPoint(
+ x: offsetOrigin.x + vertexSize.width / 2.0,
+ y: offsetOrigin.y + vertexSize.height / 2.0
+ )
+ let adjustedDestination = CGPoint(
+ x: offsetDestination.x + vertexSize.width / 2.0,
+ y: offsetDestination.y + vertexSize.height / 2.0
+ )
+
+ lines.append(Line(start: adjustedOrigin, end: adjustedDestination))
+ }
+
+ lines.append(contentsOf: axisLines)
+
+ return lines
+ }
+
+ private static func countCollisions(labelRect: CGRect, lines: [Line]) -> Int {
+ var collisions = 0
+
+ for line in lines {
+ if lineIntersectsRect(line: line, rect: labelRect) {
+ collisions += 1
+ }
+ }
+
+ return collisions
+ }
+
+ private static func lineIntersectsRect(line: Line, rect: CGRect) -> Bool {
+ let rectLines = [
+ Line(start: CGPoint(x: rect.minX, y: rect.minY), end: CGPoint(x: rect.maxX, y: rect.minY)),
+ Line(start: CGPoint(x: rect.maxX, y: rect.minY), end: CGPoint(x: rect.maxX, y: rect.maxY)),
+ Line(start: CGPoint(x: rect.maxX, y: rect.maxY), end: CGPoint(x: rect.minX, y: rect.maxY)),
+ Line(start: CGPoint(x: rect.minX, y: rect.maxY), end: CGPoint(x: rect.minX, y: rect.minY)),
+ ]
+
+ for rectLine in rectLines {
+ if linesIntersect(line1: line, line2: rectLine) {
+ return true
+ }
+ }
+
+ return rect.contains(line.start) || rect.contains(line.end)
+ }
+
+ private static func linesIntersect(line1: Line, line2: Line) -> Bool {
+ let x1 = line1.start.x
+ let y1 = line1.start.y
+ let x2 = line1.end.x
+ let y2 = line1.end.y
+ let x3 = line2.start.x
+ let y3 = line2.start.y
+ let x4 = line2.end.x
+ let y4 = line2.end.y
+
+ let denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)
+
+ if abs(denom) < 1e-10 {
+ return false
+ }
+
+ let t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / denom
+ let u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / denom
+
+ return t >= 0 && t <= 1 && u >= 0 && u <= 1
+ }
+
+ private static func w(_ dimension: CGFloat, mapSize: CGSize) -> CGFloat {
+ max(0.0, min(mapSize.width, dimension * mapSize.width / 100.0))
+ }
+
+ private static func collectNoteRects(
+ notes: [Note],
+ mapSize: CGSize
+ ) -> [CGRect] {
+ return notes.map { note in
+ let notePixelPos = CGPoint(
+ x: w(note.position.x, mapSize: mapSize),
+ y: h(note.position.y, mapSize: mapSize)
+ )
+ let noteSize = calculateLabelSize(for: note.text)
+ return CGRect(origin: notePixelPos, size: noteSize)
+ }
+ }
+
+ private static func countNoteCollisions(labelRect: CGRect, noteRects: [CGRect]) -> Int {
+ var collisions = 0
+
+ for noteRect in noteRects {
+ if labelRect.intersects(noteRect) {
+ collisions += 1
+ }
+ }
+
+ return collisions
+ }
+
+ private static func h(_ dimension: CGFloat, mapSize: CGSize) -> CGFloat {
+ max(0.0, min(mapSize.height, dimension * mapSize.height / 100.0))
+ }
+}
+
+struct Line {
+ let start: CGPoint
+ let end: CGPoint
+}
diff --git a/Map/Localizable.xcstrings b/Map/Localizable.xcstrings
index fc9ef7c..f2c0b6c 100644
--- a/Map/Localizable.xcstrings
+++ b/Map/Localizable.xcstrings
@@ -147,6 +147,17 @@
}
}
},
+ "document.inactive" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Document not active"
+ }
+ }
+ }
+ },
"evolution_picker.label" : {
"extractionState" : "manual",
"localizations" : {
@@ -736,1324 +747,1324 @@
}
}
},
- "stage_type.certainty" : {
+ "stage_type.behavior.stage_i" : {
+ "comment" : "First behavior stage - uncertain about when and how to use the component",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Certainty"
+ "value" : "Uncertain when to use"
}
}
}
},
- "stage_type.comparison" : {
+ "stage_type.behavior.stage_ii" : {
+ "comment" : "Second behavior stage - learning when to use the component appropriately",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Comparison"
+ "value" : "Learning when to use"
}
}
}
},
- "stage_type.cynefin" : {
+ "stage_type.behavior.stage_iii" : {
+ "comment" : "Third behavior stage - learning through practical use and experience",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Cynefin"
+ "value" : "Learning through use"
}
}
}
},
- "stage_type.cynefin.i" : {
+ "stage_type.behavior.stage_iv" : {
+ "comment" : "Fourth behavior stage - known and common usage patterns that are well understood",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Chaotic"
+ "value" : "Known / common usage"
}
}
}
},
- "stage_type.cynefin.ii" : {
+ "stage_type.certainty" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Complex"
+ "value" : "Certainty"
}
}
}
},
- "stage_type.cynefin.iii" : {
+ "stage_type.certainty.stage_i" : {
+ "comment" : "First certainty stage - poorly understood components with high uncertainty",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Complicated"
+ "value" : "Poorly Understood / exploring the unknown"
}
}
}
},
- "stage_type.cynefin.iv" : {
+ "stage_type.certainty.stage_ii" : {
+ "comment" : "Second certainty stage - rapid learning and discovery leading to better understanding",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Clear"
+ "value" : "Rapid Increase In Learning / discovery becomes refining"
}
}
}
},
- "stage_type.data" : {
+ "stage_type.certainty.stage_iii" : {
+ "comment" : "Third certainty stage - increased practical use leading to better fit for purpose",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Data"
+ "value" : "Rapid increase in use / increasing fit for purpose"
}
}
}
},
- "stage_type.decision_drivers" : {
+ "stage_type.certainty.stage_iv" : {
+ "comment" : "Fourth certainty stage - commonly understood components with predictable behavior",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Decision Drivers"
+ "value" : "Commonly understood (in terms of use)"
}
}
}
},
- "stage_type.efficiency" : {
+ "stage_type.comparison" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Efficiency"
+ "value" : "Comparison"
}
}
}
},
- "stage_type.failure" : {
+ "stage_type.comparison.stage_i" : {
+ "comment" : "First comparison stage - constantly changing, differential, and unstable",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Failure"
+ "value" : "Constantly changing / a differential / unstable"
}
}
}
},
- "stage_type.focus_of_value" : {
+ "stage_type.comparison.stage_ii" : {
+ "comment" : "Second comparison stage - learning from others with some evidential support",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Focus Of Value"
+ "value" : "Learning from others / testing the water / some evidential support"
}
}
}
},
- "stage_type.general" : {
+ "stage_type.comparison.stage_iii" : {
+ "comment" : "Third comparison stage - competing models with feature differences and evidence",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Activities"
+ "value" : "Competing models / feature difference / evidential support"
}
}
}
},
- "stage_type.knowledge" : {
+ "stage_type.comparison.stage_iv" : {
+ "comment" : "Fourth comparison stage - essential components where advantage is operational",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Knowledge"
+ "value" : "Essential / any advantage is operational / accepted norm"
}
}
}
},
- "stage_type.knowledge_management" : {
+ "stage_type.cynefin" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Knowledge Management"
+ "value" : "Cynefin"
}
}
}
},
- "stage_type.market" : {
+ "stage_type.cynefin.i" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Market"
+ "value" : "Chaotic"
}
}
}
},
- "stage_type.market_action" : {
+ "stage_type.cynefin.ii" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Market Action"
+ "value" : "Complex"
}
}
}
},
- "stage_type.market_perception" : {
+ "stage_type.cynefin.iii" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Market Perception"
+ "value" : "Complicated"
}
}
}
},
- "stage_type.perception_in_industry" : {
+ "stage_type.cynefin.iv" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Perception In Industry"
+ "value" : "Clear"
}
}
}
},
- "stage_type.placeholder" : {
+ "stage_type.data" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Evolution Stage"
+ "value" : "Data"
}
}
}
},
- "stage_type.placeholder.i" : {
+ "stage_type.data.stage_i" : {
+ "comment" : "First evolution stage for data - unmodeled, raw data without clear structure or understanding",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Stage I"
+ "value" : "Unmodelled"
}
}
}
},
- "stage_type.placeholder.ii" : {
+ "stage_type.data.stage_ii" : {
+ "comment" : "Second evolution stage for data - divergent data models being explored and tested",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Stage II"
+ "value" : "Divergent"
}
}
}
},
- "stage_type.placeholder.iii" : {
+ "stage_type.data.stage_iii" : {
+ "comment" : "Third evolution stage for data - convergent data models that are becoming standardized",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Stage III"
+ "value" : "Convergent"
}
}
}
},
- "stage_type.placeholder.iv" : {
+ "stage_type.data.stage_iv" : {
+ "comment" : "Fourth evolution stage for data - fully modeled data with well-defined schemas and standards",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Stage IV"
+ "value" : "Modelled"
}
}
}
},
- "stage_type.practice" : {
+ "stage_type.decision_drivers" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Practice"
+ "value" : "Decision Drivers"
}
}
}
},
- "stage_type.publication_types" : {
+ "stage_type.decision_drivers.stage_i" : {
+ "comment" : "First decision drivers stage - decisions driven by heritage and culture",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Publication Types"
+ "value" : "Heritage / culture"
}
}
}
},
- "stage_type.section.characteristics" : {
+ "stage_type.decision_drivers.stage_ii" : {
+ "comment" : "Second decision drivers stage - decisions based on analysis and synthesis",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Characteristics"
+ "value" : "Analyses & synthesis"
}
}
}
},
- "stage_type.section.custom" : {
+ "stage_type.decision_drivers.stage_iii" : {
+ "comment" : "Third decision drivers stage - continued analysis and synthesis approaches",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Custom"
+ "value" : "Analyses & synthesis"
}
}
}
},
- "stage_type.section.general_properties" : {
+ "stage_type.decision_drivers.stage_iv" : {
+ "comment" : "Fourth decision drivers stage - decisions based on previous experience and patterns",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "General Properties"
+ "value" : "Previous Experience"
}
}
}
},
- "stage_type.section.types" : {
+ "stage_type.efficiency" : {
+ "extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Types"
+ "value" : "Efficiency"
}
}
}
},
- "stage_type.ubiquity" : {
+ "stage_type.efficiency.stage_i" : {
+ "comment" : "First efficiency stage - reducing cost of change through experimentation",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Ubiquity"
+ "value" : "Reducing the cost of change (experimentation)"
}
}
}
},
- "stage_type.understanding" : {
+ "stage_type.efficiency.stage_ii" : {
+ "comment" : "Second efficiency stage - reducing cost of waste through learning",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Understanding"
+ "value" : "Reducing cost of waste (Learning)"
}
}
}
},
- "stage_type.user_perception" : {
+ "stage_type.efficiency.stage_iii" : {
+ "comment" : "Third efficiency stage - reducing cost of waste through continued learning",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "User Perception"
+ "value" : "Reducing cost of waste (Learning)"
}
}
}
},
- "stage_type.general.stage_i" : {
- "comment" : "First evolution stage for general activities - novel, experimental components with high uncertainty",
+ "stage_type.efficiency.stage_iv" : {
+ "comment" : "Fourth efficiency stage - reducing cost of deviation through volume operations",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Genesis"
+ "value" : "Reducing cost of deviation (Volume)"
}
}
}
},
- "stage_type.general.stage_ii" : {
- "comment" : "Second evolution stage for general activities - custom-built solutions emerging from genesis",
+ "stage_type.failure" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Custom"
+ "value" : "Failure"
}
}
}
},
- "stage_type.general.stage_iii" : {
- "comment" : "Third evolution stage for general activities - standardized products available for purchase or rental",
+ "stage_type.failure.stage_i" : {
+ "comment" : "First failure stage - high failure rate that is tolerated and assumed",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Product (+rental)"
+ "value" : "High / tolerated / assumed to be wrong"
}
}
}
},
- "stage_type.general.stage_iv" : {
- "comment" : "Fourth evolution stage for general activities - commoditized utilities with standard interfaces",
+ "stage_type.failure.stage_ii" : {
+ "comment" : "Second failure stage - moderate failure rate that is disappointing but not surprising",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Commodity (+utility)"
+ "value" : "Moderate / unsurprising if wrong but disappointed"
}
}
}
},
- "stage_type.practice.stage_i" : {
- "comment" : "First evolution stage for practices - novel, experimental practices with uncertain outcomes",
+ "stage_type.failure.stage_iii" : {
+ "comment" : "Third failure stage - failure not tolerated with focus on constant improvement",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Novel"
+ "value" : "Not tolerated / focus on constant improvement / assumed to be in the right direction / resistance to changing the model"
}
}
}
},
- "stage_type.practice.stage_ii" : {
- "comment" : "Second evolution stage for practices - emerging practices showing some promise",
+ "stage_type.failure.stage_iv" : {
+ "comment" : "Fourth failure stage - surprise at failure with focus on operational efficiency",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Emerging"
+ "value" : "Surprised by failure / focus on operational efficiency"
}
}
}
},
- "stage_type.practice.stage_iii" : {
- "comment" : "Third evolution stage for practices - good practices that are proven and reliable",
+ "stage_type.focus_of_value" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Good"
+ "value" : "Focus Of Value"
}
}
}
},
- "stage_type.practice.stage_iv" : {
- "comment" : "Fourth evolution stage for practices - best practices that are widely adopted and standardized",
+ "stage_type.focus_of_value.stage_i" : {
+ "comment" : "First value focus stage - high future worth requiring immediate investment",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Best"
+ "value" : "High future worth but immediate investment"
}
}
}
},
- "stage_type.data.stage_i" : {
- "comment" : "First evolution stage for data - unmodeled, raw data without clear structure or understanding",
+ "stage_type.focus_of_value.stage_ii" : {
+ "comment" : "Second value focus stage - seeking ways to profit and confirm return on investment",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Unmodelled"
+ "value" : "Seeking ways to profit and a ROI / seeking confirmation of value"
}
}
}
},
- "stage_type.data.stage_ii" : {
- "comment" : "Second evolution stage for data - divergent data models being explored and tested",
+ "stage_type.focus_of_value.stage_iii" : {
+ "comment" : "Third value focus stage - high profitability with focus on exploitation of valuable models",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Divergent"
+ "value" : "High profitability per unit / a valuable model / a feeling of understanding / focus on exploitation"
}
}
}
},
- "stage_type.data.stage_iii" : {
- "comment" : "Third evolution stage for data - convergent data models that are becoming standardized",
+ "stage_type.focus_of_value.stage_iv" : {
+ "comment" : "Fourth value focus stage - high volume with reducing margins as essential infrastructure",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Convergent"
+ "value" : "High volume / reducing margin / important but invisible / an essential component of something more complex"
}
}
}
},
- "stage_type.data.stage_iv" : {
- "comment" : "Fourth evolution stage for data - fully modeled data with well-defined schemas and standards",
+ "stage_type.general" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Modelled"
+ "value" : "Activities"
}
}
}
},
- "stage_type.knowledge.stage_i" : {
- "comment" : "First evolution stage for knowledge - conceptual ideas without empirical validation",
+ "stage_type.general.stage_i" : {
+ "comment" : "First evolution stage for general activities - novel, experimental components with high uncertainty",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Concept"
+ "value" : "Genesis"
}
}
}
},
- "stage_type.knowledge.stage_ii" : {
- "comment" : "Second evolution stage for knowledge - hypotheses that can be tested and validated",
+ "stage_type.general.stage_ii" : {
+ "comment" : "Second evolution stage for general activities - custom-built solutions emerging from genesis",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Hypothesis"
+ "value" : "Custom"
}
}
}
},
- "stage_type.knowledge.stage_iii" : {
- "comment" : "Third evolution stage for knowledge - theoretical frameworks supported by evidence",
+ "stage_type.general.stage_iii" : {
+ "comment" : "Third evolution stage for general activities - standardized products available for purchase or rental",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Theory"
+ "value" : "Product (+rental)"
}
}
}
},
- "stage_type.knowledge.stage_iv" : {
- "comment" : "Fourth evolution stage for knowledge - accepted knowledge that is widely understood and proven",
+ "stage_type.general.stage_iv" : {
+ "comment" : "Fourth evolution stage for general activities - commoditized utilities with standard interfaces",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Accepted"
+ "value" : "Commodity (+utility)"
}
}
}
},
- "stage_type.ubiquity.stage_i" : {
- "comment" : "First ubiquity stage - rare components with limited adoption",
+ "stage_type.knowledge" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Rare"
+ "value" : "Knowledge"
}
}
}
},
- "stage_type.ubiquity.stage_ii" : {
- "comment" : "Second ubiquity stage - slowly increasing adoption and awareness",
+ "stage_type.knowledge_management" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Slowly Increasing"
+ "value" : "Knowledge Management"
}
}
}
},
- "stage_type.ubiquity.stage_iii" : {
- "comment" : "Third ubiquity stage - rapidly increasing adoption as value becomes clear",
+ "stage_type.knowledge_management.stage_i" : {
+ "comment" : "First knowledge management stage - uncertain knowledge with unclear application",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Rapidly Increasing"
+ "value" : "Uncertain"
}
}
}
},
- "stage_type.ubiquity.stage_iv" : {
- "comment" : "Fourth ubiquity stage - widespread adoption throughout the applicable market or ecosystem",
+ "stage_type.knowledge_management.stage_ii" : {
+ "comment" : "Second knowledge management stage - learning through use and testing predictions",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Widespread in the applicable market / ecosystem"
+ "value" : "Learning on use / focused on testing prediction"
}
}
}
},
- "stage_type.certainty.stage_i" : {
- "comment" : "First certainty stage - poorly understood components with high uncertainty",
+ "stage_type.knowledge_management.stage_iii" : {
+ "comment" : "Third knowledge management stage - operational learning with prediction and verification",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Poorly Understood / exploring the unknown"
+ "value" : "Learning on operation / using prediction / verification"
}
}
}
},
- "stage_type.certainty.stage_ii" : {
- "comment" : "Second certainty stage - rapid learning and discovery leading to better understanding",
+ "stage_type.knowledge_management.stage_iv" : {
+ "comment" : "Fourth knowledge management stage - known and accepted knowledge that is well understood",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Rapid Increase In Learning / discovery becomes refining"
+ "value" : "Known / accepted"
}
}
}
},
- "stage_type.certainty.stage_iii" : {
- "comment" : "Third certainty stage - increased practical use leading to better fit for purpose",
+ "stage_type.knowledge.stage_i" : {
+ "comment" : "First evolution stage for knowledge - conceptual ideas without empirical validation",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Rapid increase in use / increasing fit for purpose"
+ "value" : "Concept"
}
}
}
},
- "stage_type.certainty.stage_iv" : {
- "comment" : "Fourth certainty stage - commonly understood components with predictable behavior",
+ "stage_type.knowledge.stage_ii" : {
+ "comment" : "Second evolution stage for knowledge - hypotheses that can be tested and validated",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Commonly understood (in terms of use)"
+ "value" : "Hypothesis"
}
}
}
},
- "stage_type.publication_types.stage_i" : {
- "comment" : "First publication stage - describing novel discoveries and unknown frontiers",
+ "stage_type.knowledge.stage_iii" : {
+ "comment" : "Third evolution stage for knowledge - theoretical frameworks supported by evidence",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Describe the wonder of the thing / the discovery of some marvel / a new land / an unknown frontier"
+ "value" : "Theory"
}
}
}
},
- "stage_type.publication_types.stage_ii" : {
- "comment" : "Second publication stage - focused on building and learning with multiple competing models",
+ "stage_type.knowledge.stage_iv" : {
+ "comment" : "Fourth evolution stage for knowledge - accepted knowledge that is widely understood and proven",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Focused on build / construct / awareness and learning / many models of explanation / no accepted forms / a wild west"
+ "value" : "Accepted"
}
}
}
},
- "stage_type.publication_types.stage_iii" : {
- "comment" : "Third publication stage - maintenance and operational focus with feature comparisons",
+ "stage_type.market" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Maintenance / operations / installation / comparison between competing forms / feature analysis"
+ "value" : "Market"
}
}
}
},
- "stage_type.publication_types.stage_iv" : {
- "comment" : "Fourth publication stage - focused on practical use of accepted, nearly invisible components",
+ "stage_type.market_action" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Focused on use / increasingly an accepted, almost invisible component"
+ "value" : "Market Action"
}
}
}
},
- "stage_type.market.stage_i" : {
- "comment" : "First market stage - undefined market with unclear value proposition",
+ "stage_type.market_action.stage_i" : {
+ "comment" : "First market action stage - gambling and gut-driven decision making",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Undefined Market"
+ "value" : "Gambling / driven by gut"
}
}
}
},
- "stage_type.market.stage_ii" : {
- "comment" : "Second market stage - forming market with competing approaches and models",
+ "stage_type.market_action.stage_ii" : {
+ "comment" : "Second market action stage - exploring discovered value opportunities",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Forming Market / an array of competing forms and models of understanding"
+ "value" : "Exploring a \"found\" value"
}
}
}
},
- "stage_type.market.stage_iii" : {
- "comment" : "Third market stage - growing market with consolidation toward accepted forms",
+ "stage_type.market_action.stage_iii" : {
+ "comment" : "Third market action stage - market analysis and listening to customer feedback",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Growing Market / consolidation to a few competing but more accepted forms"
+ "value" : "Market analysis / listening to customers"
}
}
}
},
- "stage_type.market.stage_iv" : {
- "comment" : "Fourth market stage - mature market with stabilized and accepted forms",
+ "stage_type.market_action.stage_iv" : {
+ "comment" : "Fourth market action stage - metric-driven approach building what is needed",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Mature Market / stabilised to an accepted form"
+ "value" : "Metric driven / build what is needed"
}
}
}
},
- "stage_type.knowledge_management.stage_i" : {
- "comment" : "First knowledge management stage - uncertain knowledge with unclear application",
+ "stage_type.market_perception" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Uncertain"
+ "value" : "Market Perception"
}
}
}
},
- "stage_type.knowledge_management.stage_ii" : {
- "comment" : "Second knowledge management stage - learning through use and testing predictions",
+ "stage_type.market_perception.stage_i" : {
+ "comment" : "First market perception stage - chaotic, non-linear domain of experimental 'crazy' ideas",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Learning on use / focused on testing prediction"
+ "value" : "Chaotic (non-linear) / domain of the \"crazy\""
}
}
}
},
- "stage_type.knowledge_management.stage_iii" : {
- "comment" : "Third knowledge management stage - operational learning with prediction and verification",
+ "stage_type.market_perception.stage_ii" : {
+ "comment" : "Second market perception stage - domain of experts with specialized knowledge",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Learning on operation / using prediction / verification"
+ "value" : "Domain of \"experts\""
}
}
}
},
- "stage_type.knowledge_management.stage_iv" : {
- "comment" : "Fourth knowledge management stage - known and accepted knowledge that is well understood",
+ "stage_type.market_perception.stage_iii" : {
+ "comment" : "Third market perception stage - increasing expectations of use by professionals",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Known / accepted"
+ "value" : "Increasing expectation of use / domain of \"professionals\""
}
}
}
},
- "stage_type.market_perception.stage_i" : {
- "comment" : "First market perception stage - chaotic, non-linear domain of experimental 'crazy' ideas",
+ "stage_type.market_perception.stage_iv" : {
+ "comment" : "Fourth market perception stage - ordered, linear domain with trivial, formulaic applications",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Chaotic (non-linear) / domain of the \"crazy\""
+ "value" : "Ordered (appearance of being linear) / trivial / formula to be applied"
}
}
}
},
- "stage_type.market_perception.stage_ii" : {
- "comment" : "Second market perception stage - domain of experts with specialized knowledge",
+ "stage_type.market.stage_i" : {
+ "comment" : "First market stage - undefined market with unclear value proposition",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Domain of \"experts\""
+ "value" : "Undefined Market"
}
}
}
},
- "stage_type.market_perception.stage_iii" : {
- "comment" : "Third market perception stage - increasing expectations of use by professionals",
+ "stage_type.market.stage_ii" : {
+ "comment" : "Second market stage - forming market with competing approaches and models",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Increasing expectation of use / domain of \"professionals\""
+ "value" : "Forming Market / an array of competing forms and models of understanding"
}
}
}
},
- "stage_type.market_perception.stage_iv" : {
- "comment" : "Fourth market perception stage - ordered, linear domain with trivial, formulaic applications",
+ "stage_type.market.stage_iii" : {
+ "comment" : "Third market stage - growing market with consolidation toward accepted forms",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Ordered (appearance of being linear) / trivial / formula to be applied"
+ "value" : "Growing Market / consolidation to a few competing but more accepted forms"
}
}
}
},
- "stage_type.user_perception.stage_i" : {
- "comment" : "First user perception stage - different, confusing, exciting, and potentially dangerous",
+ "stage_type.market.stage_iv" : {
+ "comment" : "Fourth market stage - mature market with stabilized and accepted forms",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Different / confusing / exciting / surprising / dangerous"
+ "value" : "Mature Market / stabilised to an accepted form"
}
}
}
},
- "stage_type.user_perception.stage_ii" : {
- "comment" : "Second user perception stage - leading edge technology with uncertain results",
+ "stage_type.perception_in_industry" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Leading edge / emerging / unceirtanty over results"
+ "value" : "Perception In Industry"
}
}
}
},
- "stage_type.user_perception.stage_iii" : {
- "comment" : "Third user perception stage - increasingly common with disappointment if not available",
+ "stage_type.perception_in_industry.stage_i" : {
+ "comment" : "First industry perception stage - future competitive advantage that is unpredictable and unknown",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Increasingly common / disappointed if not used or available / feeling left behind"
+ "value" : "Future source of competitive advantage / unpredictable / unknown"
}
}
}
},
- "stage_type.user_perception.stage_iv" : {
- "comment" : "Fourth user perception stage - standard and expected, with shock if not available",
+ "stage_type.perception_in_industry.stage_ii" : {
+ "comment" : "Second industry perception stage - seen as competitive advantage with ROI and case examples",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Standard / expected / feeling of shock if not used"
+ "value" : "Seen as a scompetitive advantage / a differential / ROI / case examples"
}
}
}
},
- "stage_type.perception_in_industry.stage_i" : {
- "comment" : "First industry perception stage - future competitive advantage that is unpredictable and unknown",
+ "stage_type.perception_in_industry.stage_iii" : {
+ "comment" : "Third industry perception stage - advantage through implementation and feature differentiation",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Future source of competitive advantage / unpredictable / unknown"
+ "value" : "Advantage through implementation / features / this model is better than that"
}
}
}
},
- "stage_type.perception_in_industry.stage_ii" : {
- "comment" : "Second industry perception stage - seen as competitive advantage with ROI and case examples",
+ "stage_type.perception_in_industry.stage_iv" : {
+ "comment" : "Fourth industry perception stage - cost of doing business with accepted, defined models",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Seen as a scompetitive advantage / a differential / ROI / case examples"
+ "value" : "Cost of doing business / accepted / specific defined models"
}
}
}
},
- "stage_type.perception_in_industry.stage_iii" : {
- "comment" : "Third industry perception stage - advantage through implementation and feature differentiation",
+ "stage_type.placeholder" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Advantage through implementation / features / this model is better than that"
+ "value" : "Evolution Stage"
}
}
}
},
- "stage_type.perception_in_industry.stage_iv" : {
- "comment" : "Fourth industry perception stage - cost of doing business with accepted, defined models",
+ "stage_type.placeholder.i" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Cost of doing business / accepted / specific defined models"
+ "value" : "Stage I"
}
}
}
},
- "stage_type.focus_of_value.stage_i" : {
- "comment" : "First value focus stage - high future worth requiring immediate investment",
+ "stage_type.placeholder.ii" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "High future worth but immediate investment"
+ "value" : "Stage II"
}
}
}
},
- "stage_type.focus_of_value.stage_ii" : {
- "comment" : "Second value focus stage - seeking ways to profit and confirm return on investment",
+ "stage_type.placeholder.iii" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Seeking ways to profit and a ROI / seeking confirmation of value"
+ "value" : "Stage III"
}
}
}
},
- "stage_type.focus_of_value.stage_iii" : {
- "comment" : "Third value focus stage - high profitability with focus on exploitation of valuable models",
+ "stage_type.placeholder.iv" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "High profitability per unit / a valuable model / a feeling of understanding / focus on exploitation"
+ "value" : "Stage IV"
}
}
}
},
- "stage_type.focus_of_value.stage_iv" : {
- "comment" : "Fourth value focus stage - high volume with reducing margins as essential infrastructure",
+ "stage_type.practice" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "High volume / reducing margin / important but invisible / an essential component of something more complex"
+ "value" : "Practice"
}
}
}
},
- "stage_type.understanding.stage_i" : {
- "comment" : "First understanding stage - poorly understood and unpredictable components",
+ "stage_type.practice.stage_i" : {
+ "comment" : "First evolution stage for practices - novel, experimental practices with uncertain outcomes",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Poorly Understood / unpredictable"
+ "value" : "Novel"
}
}
}
},
- "stage_type.understanding.stage_ii" : {
- "comment" : "Second understanding stage - increasing understanding with development of measures",
+ "stage_type.practice.stage_ii" : {
+ "comment" : "Second evolution stage for practices - emerging practices showing some promise",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Increasing understanding / development of measures"
+ "value" : "Emerging"
}
}
}
},
- "stage_type.understanding.stage_iii" : {
- "comment" : "Third understanding stage - increasing education with constant refinement of needs",
+ "stage_type.practice.stage_iii" : {
+ "comment" : "Third evolution stage for practices - good practices that are proven and reliable",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Increasing education / constant refinement of needs / measures"
+ "value" : "Good"
}
}
}
},
- "stage_type.understanding.stage_iv" : {
- "comment" : "Fourth understanding stage - well-defined, stable, and measurable components",
+ "stage_type.practice.stage_iv" : {
+ "comment" : "Fourth evolution stage for practices - best practices that are widely adopted and standardized",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Believed to be well defined / stable / measurable"
+ "value" : "Best"
}
}
}
},
- "stage_type.comparison.stage_i" : {
- "comment" : "First comparison stage - constantly changing, differential, and unstable",
+ "stage_type.publication_types" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Constantly changing / a differential / unstable"
+ "value" : "Publication Types"
}
}
}
},
- "stage_type.comparison.stage_ii" : {
- "comment" : "Second comparison stage - learning from others with some evidential support",
+ "stage_type.publication_types.stage_i" : {
+ "comment" : "First publication stage - describing novel discoveries and unknown frontiers",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Learning from others / testing the water / some evidential support"
+ "value" : "Describe the wonder of the thing / the discovery of some marvel / a new land / an unknown frontier"
}
}
}
},
- "stage_type.comparison.stage_iii" : {
- "comment" : "Third comparison stage - competing models with feature differences and evidence",
+ "stage_type.publication_types.stage_ii" : {
+ "comment" : "Second publication stage - focused on building and learning with multiple competing models",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Competing models / feature difference / evidential support"
+ "value" : "Focused on build / construct / awareness and learning / many models of explanation / no accepted forms / a wild west"
}
}
}
},
- "stage_type.comparison.stage_iv" : {
- "comment" : "Fourth comparison stage - essential components where advantage is operational",
+ "stage_type.publication_types.stage_iii" : {
+ "comment" : "Third publication stage - maintenance and operational focus with feature comparisons",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Essential / any advantage is operational / accepted norm"
+ "value" : "Maintenance / operations / installation / comparison between competing forms / feature analysis"
}
}
}
},
- "stage_type.failure.stage_i" : {
- "comment" : "First failure stage - high failure rate that is tolerated and assumed",
+ "stage_type.publication_types.stage_iv" : {
+ "comment" : "Fourth publication stage - focused on practical use of accepted, nearly invisible components",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "High / tolerated / assumed to be wrong"
+ "value" : "Focused on use / increasingly an accepted, almost invisible component"
}
}
}
},
- "stage_type.failure.stage_ii" : {
- "comment" : "Second failure stage - moderate failure rate that is disappointing but not surprising",
+ "stage_type.section.characteristics" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Moderate / unsurprising if wrong but disappointed"
+ "value" : "Characteristics"
}
}
}
},
- "stage_type.failure.stage_iii" : {
- "comment" : "Third failure stage - failure not tolerated with focus on constant improvement",
+ "stage_type.section.custom" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Not tolerated / focus on constant improvement / assumed to be in the right direction / resistance to changing the model"
+ "value" : "Custom"
}
}
}
},
- "stage_type.failure.stage_iv" : {
- "comment" : "Fourth failure stage - surprise at failure with focus on operational efficiency",
+ "stage_type.section.general_properties" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Surprised by failure / focus on operational efficiency"
+ "value" : "General Properties"
}
}
}
},
- "stage_type.market_action.stage_i" : {
- "comment" : "First market action stage - gambling and gut-driven decision making",
- "extractionState" : "manual",
+ "stage_type.section.types" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Gambling / driven by gut"
+ "value" : "Types"
}
}
}
},
- "stage_type.market_action.stage_ii" : {
- "comment" : "Second market action stage - exploring discovered value opportunities",
+ "stage_type.ubiquity" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Exploring a \"found\" value"
+ "value" : "Ubiquity"
}
}
}
},
- "stage_type.market_action.stage_iii" : {
- "comment" : "Third market action stage - market analysis and listening to customer feedback",
+ "stage_type.ubiquity.stage_i" : {
+ "comment" : "First ubiquity stage - rare components with limited adoption",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Market analysis / listening to customers"
+ "value" : "Rare"
}
}
}
},
- "stage_type.market_action.stage_iv" : {
- "comment" : "Fourth market action stage - metric-driven approach building what is needed",
+ "stage_type.ubiquity.stage_ii" : {
+ "comment" : "Second ubiquity stage - slowly increasing adoption and awareness",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Metric driven / build what is needed"
+ "value" : "Slowly Increasing"
}
}
}
},
- "stage_type.efficiency.stage_i" : {
- "comment" : "First efficiency stage - reducing cost of change through experimentation",
+ "stage_type.ubiquity.stage_iii" : {
+ "comment" : "Third ubiquity stage - rapidly increasing adoption as value becomes clear",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Reducing the cost of change (experimentation)"
+ "value" : "Rapidly Increasing"
}
}
}
},
- "stage_type.efficiency.stage_ii" : {
- "comment" : "Second efficiency stage - reducing cost of waste through learning",
+ "stage_type.ubiquity.stage_iv" : {
+ "comment" : "Fourth ubiquity stage - widespread adoption throughout the applicable market or ecosystem",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Reducing cost of waste (Learning)"
+ "value" : "Widespread in the applicable market / ecosystem"
}
}
}
},
- "stage_type.efficiency.stage_iii" : {
- "comment" : "Third efficiency stage - reducing cost of waste through continued learning",
+ "stage_type.understanding" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Reducing cost of waste (Learning)"
+ "value" : "Understanding"
}
}
}
},
- "stage_type.efficiency.stage_iv" : {
- "comment" : "Fourth efficiency stage - reducing cost of deviation through volume operations",
+ "stage_type.understanding.stage_i" : {
+ "comment" : "First understanding stage - poorly understood and unpredictable components",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Reducing cost of deviation (Volume)"
+ "value" : "Poorly Understood / unpredictable"
}
}
}
},
- "stage_type.decision_drivers.stage_i" : {
- "comment" : "First decision drivers stage - decisions driven by heritage and culture",
+ "stage_type.understanding.stage_ii" : {
+ "comment" : "Second understanding stage - increasing understanding with development of measures",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Heritage / culture"
+ "value" : "Increasing understanding / development of measures"
}
}
}
},
- "stage_type.decision_drivers.stage_ii" : {
- "comment" : "Second decision drivers stage - decisions based on analysis and synthesis",
+ "stage_type.understanding.stage_iii" : {
+ "comment" : "Third understanding stage - increasing education with constant refinement of needs",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Analyses & synthesis"
+ "value" : "Increasing education / constant refinement of needs / measures"
}
}
}
},
- "stage_type.decision_drivers.stage_iii" : {
- "comment" : "Third decision drivers stage - continued analysis and synthesis approaches",
+ "stage_type.understanding.stage_iv" : {
+ "comment" : "Fourth understanding stage - well-defined, stable, and measurable components",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Analyses & synthesis"
+ "value" : "Believed to be well defined / stable / measurable"
}
}
}
},
- "stage_type.decision_drivers.stage_iv" : {
- "comment" : "Fourth decision drivers stage - decisions based on previous experience and patterns",
+ "stage_type.user_perception" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Previous Experience"
+ "value" : "User Perception"
}
}
}
},
- "stage_type.behavior.stage_i" : {
- "comment" : "First behavior stage - uncertain about when and how to use the component",
+ "stage_type.user_perception.stage_i" : {
+ "comment" : "First user perception stage - different, confusing, exciting, and potentially dangerous",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Uncertain when to use"
+ "value" : "Different / confusing / exciting / surprising / dangerous"
}
}
}
},
- "stage_type.behavior.stage_ii" : {
- "comment" : "Second behavior stage - learning when to use the component appropriately",
+ "stage_type.user_perception.stage_ii" : {
+ "comment" : "Second user perception stage - leading edge technology with uncertain results",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Learning when to use"
+ "value" : "Leading edge / emerging / unceirtanty over results"
}
}
}
},
- "stage_type.behavior.stage_iii" : {
- "comment" : "Third behavior stage - learning through practical use and experience",
+ "stage_type.user_perception.stage_iii" : {
+ "comment" : "Third user perception stage - increasingly common with disappointment if not available",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Learning through use"
+ "value" : "Increasingly common / disappointed if not used or available / feeling left behind"
}
}
}
},
- "stage_type.behavior.stage_iv" : {
- "comment" : "Fourth behavior stage - known and common usage patterns that are well understood",
+ "stage_type.user_perception.stage_iv" : {
+ "comment" : "Fourth user perception stage - standard and expected, with shock if not available",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Known / common usage"
+ "value" : "Standard / expected / feeling of shock if not used"
}
}
}
diff --git a/Map/MapApp.swift b/Map/MapApp.swift
index 54eb9be..5061807 100644
--- a/Map/MapApp.swift
+++ b/Map/MapApp.swift
@@ -12,6 +12,7 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see https://map.tranquil.systems.
+import AppKit
import Sparkle
import SwiftUI
@@ -23,7 +24,7 @@ struct MapApp: App {
var body: some Scene {
DocumentGroup(newDocument: MapDocument(text: nil)) { file in
- MapEditor(document: file.$document, url: file.fileURL)
+ LazyMapEditor(document: file.$document, url: file.fileURL)
.focusedSceneValue(\.document, file.$document)
.focusedSceneValue(\.fileURL, file.fileURL)
}
@@ -38,3 +39,67 @@ struct MapApp: App {
}
}
}
+
+struct LazyMapEditor: View {
+ @Binding var document: MapDocument
+ var url: URL?
+
+ @FocusedBinding(\.document) var focusedDocument: MapDocument?
+ @FocusedValue(\.fileURL) var focusedFileURL: URL?
+ @State private var shouldRender = false
+ @State private var renderTask: Task<Void, Never>?
+
+ var isActiveDocument: Bool {
+ guard let focusedDocument else { return false }
+ // Use URL for identification if available, otherwise fall back to text comparison
+ if let documentURL = url,
+ let focusedURL = focusedFileURL
+ {
+ return documentURL == focusedURL
+ }
+ // For new documents without URLs, use text comparison as fallback
+ return focusedDocument.text == document.text
+ }
+
+ var body: some View {
+ if isActiveDocument && shouldRender {
+ MapEditor(document: $document, url: url)
+ } else {
+ Rectangle()
+ .fill(Color(NSColor.windowBackgroundColor))
+ .overlay(
+ VStack(spacing: 16) {
+ Text("document.inactive")
+ .font(.title2)
+ .foregroundColor(.secondary)
+ if let url = url {
+ Text(url.lastPathComponent)
+ .font(.caption)
+ .foregroundColor(.secondary)
+ }
+ }
+ )
+ .onChange(of: isActiveDocument) { _, newValue in
+ renderTask?.cancel()
+ renderTask = nil
+
+ if newValue {
+ renderTask = Task {
+ try? await Task.sleep(nanoseconds: 50_000_000)
+ if !Task.isCancelled {
+ await MainActor.run {
+ shouldRender = true
+ }
+ }
+ }
+ } else {
+ shouldRender = false
+ }
+ }
+ .onAppear {
+ // Initialize focus state properly - don't render by default
+ shouldRender = false
+ }
+ }
+ }
+}
diff --git a/Map/Presentation/Base Components/MapRender/MapMask.swift b/Map/Presentation/Base Components/MapRender/MapMask.swift
index 2c064b3..b477c0a 100644
--- a/Map/Presentation/Base Components/MapRender/MapMask.swift
+++ b/Map/Presentation/Base Components/MapRender/MapMask.swift
@@ -19,6 +19,7 @@ struct MapMask: View {
let mapSize: CGSize
let vertexSize: CGSize
let vertices: [Vertex]
+ let labelPositions: [Int: CGPoint]
let padding = CGFloat(5.0)
var body: some View {
@@ -36,8 +37,9 @@ struct MapMask: View {
.clipShape(Capsule())
.offset(
CGSize(
- width: w(vertex.position.x) + vertexSize.width + padding,
- height: h(vertex.position.y) + 7.0)
+ width: labelPositions[vertex.id]?.x
+ ?? (w(vertex.position.x) + vertexSize.width + padding),
+ height: labelPositions[vertex.id]?.y ?? (h(vertex.position.y) + 7.0))
)
.blendMode(.destinationOut)
}.zIndex(1)
@@ -64,5 +66,6 @@ struct MapMask: View {
Vertex(id: 1, label: "A Square", position: CGPoint(x: 10.0, y: 20.0), shape: .square),
Vertex(id: 2, label: "A triangle", position: CGPoint(x: 25, y: 32.0), shape: .triangle),
Vertex(id: 3, label: "An X", position: CGPoint(x: 70.0, y: 70.0), shape: .x),
- ])
+ ],
+ labelPositions: [:])
}
diff --git a/Map/Presentation/Base Components/MapRender/MapVertices.swift b/Map/Presentation/Base Components/MapRender/MapVertices.swift
index f03bb79..e6617b3 100644
--- a/Map/Presentation/Base Components/MapRender/MapVertices.swift
+++ b/Map/Presentation/Base Components/MapRender/MapVertices.swift
@@ -19,6 +19,7 @@ struct MapVertices: View {
let mapSize: CGSize
let vertexSize: CGSize
let vertices: [Vertex]
+ let labelPositions: [Int: CGPoint]
let padding = CGFloat(5.0)
var onDragVertex: (Vertex, CGFloat, CGFloat) -> Void = { _, _, _ in }
@@ -36,8 +37,9 @@ struct MapVertices: View {
.foregroundColor(.Theme.Map.labelColor)
.offset(
CGSize(
- width: w(vertex.position.x) + vertexSize.width + padding,
- height: h(vertex.position.y) + 7.0))
+ width: labelPositions[vertex.id]?.x
+ ?? (w(vertex.position.x) + vertexSize.width + padding),
+ height: labelPositions[vertex.id]?.y ?? (h(vertex.position.y) + 7.0)))
}.gesture(
DragGesture()
.onChanged { value in
@@ -113,5 +115,6 @@ struct MapVertices: View {
Vertex(id: 1, label: "A Square", position: CGPoint(x: 10.0, y: 20.0), shape: .square),
Vertex(id: 2, label: "A triangle", position: CGPoint(x: 25, y: 32.0), shape: .triangle),
Vertex(id: 3, label: "An X", position: CGPoint(x: 70.0, y: 70.0), shape: .x),
- ])
+ ],
+ labelPositions: [:])
}
diff --git a/Map/Presentation/Complex Components/MapRender/MapRenderView.swift b/Map/Presentation/Complex Components/MapRender/MapRenderView.swift
index 3b97945..896fbd0 100644
--- a/Map/Presentation/Complex Components/MapRender/MapRenderView.swift
+++ b/Map/Presentation/Complex Components/MapRender/MapRenderView.swift
@@ -24,14 +24,13 @@ struct MapRenderView: View {
@AppStorage("showMapBackground") var showMapBackground: Bool = true
@AppStorage("useCustomFont") var useCustomFont: Bool = false
@AppStorage("customFontName") var customFontName: String = "Helvetica"
-
var stage: Stage {
Stage.stages(evolution)
}
- var parsedMap: ParsedMap {
- MapParser.parse(content: document.text)
- }
+ @State private var parsedMap: ParsedMap = ParsedMap.empty
+ @State private var smartLabelPositions: [Int: CGPoint] = [:]
+ @State private var parseTask: Task<Void, Never>?
let mapSize = Dimensions.Map.size
let padding = Dimensions.Map.padding
@@ -41,6 +40,54 @@ struct MapRenderView: View {
var onDragVertex: (Vertex, CGFloat, CGFloat) -> Void = { _, _, _ in }
+ private func parseMapInBackground() {
+ parseTask?.cancel()
+ let currentText = document.text
+
+ parseTask = Task {
+ // Parse on background thread
+ let parsed = await Task.detached(priority: .userInitiated) {
+ MapParser.parse(content: currentText)
+ }.value
+
+ // Calculate smart positions on background thread
+ let positions = await Task.detached(priority: .userInitiated) {
+ var positions: [Int: CGPoint] = [:]
+
+ let axisLines = [
+ Line(start: CGPoint(x: 0, y: 0), end: CGPoint(x: 0, y: mapSize.height)),
+ Line(
+ start: CGPoint(x: 0, y: mapSize.height),
+ end: CGPoint(x: mapSize.width, y: mapSize.height)),
+ ]
+
+ for vertex in parsed.vertices {
+ positions[vertex.id] = SmartLabelPositioning.calculateOptimalLabelPosition(
+ for: vertex,
+ mapSize: mapSize,
+ vertexSize: vertexSize,
+ edges: parsed.edges,
+ opportunities: parsed.opportunities,
+ inertias: parsed.inertias,
+ axisLines: axisLines,
+ allVertices: parsed.vertices,
+ notes: parsed.notes
+ )
+ }
+
+ return positions
+ }.value
+
+ // Update UI on main thread
+ if !Task.isCancelled {
+ await MainActor.run {
+ parsedMap = parsed
+ smartLabelPositions = positions
+ }
+ }
+ }
+ }
+
var body: some View {
ZStack(alignment: .topLeading) {
@@ -68,11 +115,12 @@ struct MapRenderView: View {
MapIntertias(mapSize: mapSize, vertexSize: vertexSize, inertias: parsedMap.inertias)
}.mask {
MapMask(
- mapSize: mapSize, vertexSize: vertexSize, vertices: parsedMap.vertices)
+ mapSize: mapSize, vertexSize: vertexSize, vertices: parsedMap.vertices,
+ labelPositions: smartLabelPositions)
}
MapVertices(
mapSize: mapSize, vertexSize: vertexSize, vertices: parsedMap.vertices,
- onDragVertex: onDragVertex)
+ labelPositions: smartLabelPositions, onDragVertex: onDragVertex)
MapGroups(mapSize: mapSize, vertexSize: vertexSize, groups: parsedMap.groups).drawingGroup(
opaque: true
).opacity(0.1)
@@ -84,6 +132,23 @@ struct MapRenderView: View {
width: mapSize.width + 2 * padding,
height: mapSize.height + 2 * padding, alignment: .topLeading
)
+ .onAppear {
+ parseMapInBackground()
+ }
+ .onChange(of: document.text) { _, _ in
+ parseMapInBackground()
+ }
+ .onDisappear {
+ parseTask?.cancel()
+ }
+ }
+
+ func h(_ dimension: CGFloat) -> CGFloat {
+ max(0.0, min(mapSize.height, dimension * mapSize.height / 100.0))
+ }
+
+ func w(_ dimension: CGFloat) -> CGFloat {
+ max(0.0, min(mapSize.width, dimension * mapSize.width / 100.0))
}
}