123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378 |
- import 'dart:ui';
- import 'package:o2_flutter/common/models/mindmap/mind_node.dart';
- import 'package:o2_flutter/pages/mind_map/theme/mind_map_theme.dart';
- ///
- /// 脑图结构 模版
- ///
- abstract class BaseTemplate {
- final String name;
- BaseTheme theme;
- BaseTemplate(this.name, this.theme);
- ///
- /// 计算画板的大小 根据内部节点元素大小来计算
- /// 每个模版结构不一样 计算方式不一样
- /// @return 画布大小
- ///
- Size canvasSize(NodePaintElement root);
- ///
- /// 计算每个需要画出来的元素在画布中的位置
- /// 每个模版结构不一样 位置也不一样
- /// @return 返回各个节点之间的连接线
- ///
- LinePaintElement? paintElementPosition(NodePaintElement root, Size canvasSize);
- }
- ///
- /// 默认模版
- /// 默认模版分 左右两边,第三级开始没有节点的背景只有一条底边
- ///
- class DefaultTemplate extends BaseTemplate {
- late Size rightChildrenSize;
- late Size leftChildrenSize;
- DefaultTemplate(theme) : super('default', theme);
- @override
- Size canvasSize(NodePaintElement root) {
- var width = root.nodeSize.width;
- var height = root.nodeSize.height;
- final len = root.children?.length;
- if (len == null || len == 0) {
- return Size(width*1.5, height*1.5);
- }
- var rightLen = 0;
- if (len > 2) {
- if (len % 2 == 0) {
- rightLen = len ~/ 2;
- } else {
- rightLen = len ~/ 2 + 1;
- }
- }else {
- rightLen = 1;
- }
- var rightList = root.children?.sublist(0, rightLen) ?? [];
- var leftList = len-rightLen>0 ? root.children?.sublist(rightLen, len) ?? [] : <NodePaintElement>[];
- // 右边子节点
- var rightWidth = 0.0;
- var rightHeight = 0.0;
- if(rightList.isNotEmpty) {
- for (var right in rightList) {
- var childSize = childNodeSize(
- right, theme.nodeVerticalSpace, theme.nodeHorizontalSpace);
- rightWidth =
- rightWidth > childSize.width ? rightWidth : childSize.width;
- rightHeight += childSize.height + theme.nodeHorizontalSpace;
- }
- rightHeight -= theme.nodeHorizontalSpace;
- }
- rightChildrenSize = Size(rightWidth, rightHeight);
- // 左边子节点
- var leftWidth = 0.0;
- var leftHeight = 0.0;
- if(leftList.isNotEmpty) {
- for (var left in leftList) {
- var childSize = childNodeSize(
- left, theme.nodeVerticalSpace, theme.nodeHorizontalSpace);
- leftWidth = leftWidth > childSize.width ? leftWidth : childSize.width;
- leftHeight += childSize.height + theme.nodeHorizontalSpace;
- }
- leftHeight -= theme.nodeHorizontalSpace;
- }
- leftChildrenSize = Size(leftWidth, leftHeight);
- ///
- /// root节点本身的宽+2个间隔+left和right最大的宽度*2
- /// root节点本身的高 和 left、right最大的高度*2
- ///
- width += (rightWidth > leftWidth ? rightWidth : leftWidth) * 2 + theme.nodeHorizontalSpace * 2;
- height += (rightHeight > leftHeight ? rightHeight : leftHeight) * 2;
- return Size(width*1.5, height*1.5);
- }
- ///
- /// 递归计算子节点的大小
- ///
- Size childNodeSize(NodePaintElement node, double verticalSpace, double horizontalSpace) {
- var width = node.nodeSize.width;
- var height = node.nodeSize.height;
- // 模版中第三级开始 有一条底部边线
- if (node.level>1) {
- height += theme.lineWidth;
- }
- var childrenWidth = 0.0;
- var childrenHeight = 0.0;
- if(node.children != null && node.children?.isNotEmpty == true) {
- for(var child in node.children!) {
- var childSize = childNodeSize(child, verticalSpace, horizontalSpace);
- childrenWidth = childrenWidth > childSize.width ? childrenWidth:childSize.width;
- childrenHeight += childSize.height + horizontalSpace;
- }
- childrenHeight -= horizontalSpace;
- }
- final childSize = Size(childrenWidth, childrenHeight);
- node.childrenSize = childSize;
- width += childrenWidth + verticalSpace;
- height = height>childrenHeight ? height : childrenHeight;
- return Size(width, height);
- }
- @override
- LinePaintElement? paintElementPosition(NodePaintElement root, Size canvasSize) {
- final centerPoint = Offset(canvasSize.width/2, canvasSize.height/2);
- //确定root的offset
- final rootTop = centerPoint.dy-root.nodeSize.height/2;
- final rootLeft = centerPoint.dx-root.nodeSize.width/2;
- root.offset = Offset(rootLeft, rootTop);
- nodeElementsPosition(rootTop, rootLeft, root);
- final len = root.children?.length;
- if (len == null || len == 0) {
- return null;
- }
- List<NodeConnectLine> lines = [];
- var rightLen = 0;
- if (len > 2) {
- if (len % 2 == 0) {
- rightLen = len ~/ 2;
- } else {
- rightLen = len ~/ 2 + 1;
- }
- }else {
- rightLen = 1;
- }
- final rightList = root.children?.sublist(0, rightLen) ?? [];
- final leftList = len-rightLen>0 ? root.children?.sublist(rightLen, len) ?? [] : <NodePaintElement>[];
- if(rightList.isNotEmpty) {
- final rightCenterPoint = Offset(centerPoint.dx + root.nodeSize.width/2, centerPoint.dy);
- rightNodePosition(rightList, rightChildrenSize, lines, rightCenterPoint, rightCenterPoint);
- }
- if(leftList.isNotEmpty) {
- final leftCenterPoint = Offset(centerPoint.dx - root.nodeSize.width/2, centerPoint.dy);
- leftNodePosition(leftList, leftChildrenSize, lines, leftCenterPoint, leftCenterPoint);
- }
- return LinePaintElement(lines, PaintStyle(color: theme.lineColor, strokeWidth: theme.lineWidth, style: PaintingStyle.stroke));
- }
- ///
- /// rootRightStart root节点的右边中心点 也是连接右边子节点的线条起点
- ///
- void rightNodePosition(List<NodePaintElement> rightList, Size allListSize, List<NodeConnectLine> lines, Offset parentRightCenter, Offset parentLineStart) {
- final firstNode = rightList[0];
- final firstNodeAndChildrenRegionHeight = firstNode.nodeSize.height > (firstNode.childrenSize?.height ?? 0) ?
- firstNode.nodeSize.height : (firstNode.childrenSize?.height ?? 0);
- final lastNode = rightList[rightList.length - 1];
- final lastNodeAndChildrenRegionHeight = lastNode.nodeSize.height > (lastNode.childrenSize?.height ?? 0) ?
- lastNode.nodeSize.height : (lastNode.childrenSize?.height?? 0);
- // 第一个子节点的中心点Y轴
- final centerGap = (allListSize.height - (firstNodeAndChildrenRegionHeight/2 + lastNodeAndChildrenRegionHeight/2))/2;
- final firstNodeCenterY = parentRightCenter.dy - (centerGap>0 ? centerGap : 0);
- final nodeLeft = parentRightCenter.dx + theme.nodeVerticalSpace;
- var nodeTop = 0.0; // 每个节点的top
- var y = 0.0; // node中心y
- for (var i=0;i<rightList.length;i++) {
- final rightNode = rightList[i];
- if (i == 0) {
- y = firstNodeCenterY;
- }else {
- final preNodeHeight = (rightList[i-1].childrenSize?.height ?? 0) > rightList[i-1].nodeSize.height ? (rightList[i-1].childrenSize?.height ?? 0) : rightList[i-1].nodeSize.height;
- final nodeHeight = (rightNode.childrenSize?.height ?? 0) > rightNode.nodeSize.height ? (rightNode.childrenSize?.height ?? 0) : rightNode.nodeSize.height;
- y = y + preNodeHeight /2 + nodeHeight/2 + theme.nodeHorizontalSpace;
- }
- nodeTop = y - rightNode.nodeSize.height/2;
- rightNode.offset = Offset(nodeLeft, nodeTop);
- nodeElementsPosition(nodeTop, nodeLeft, rightNode);
- final lineEndTop = rightNode.level>1 ? y + rightNode.nodeSize.height/2 + theme.lineWidth : y;
- // 第三级开始有底边
- if (rightNode.level>1) {
- lines.add(NodeConnectLine( Offset(nodeLeft, lineEndTop), Offset(nodeLeft + rightNode.nodeSize.width, lineEndTop)
- ));
- }
- lines.add(NodeConnectLine( parentLineStart, Offset(nodeLeft, lineEndTop)));
- if (rightNode.children!=null && rightNode.children?.isNotEmpty == true) {
- rightNodePosition(
- rightNode.children!,
- rightNode.childrenSize ?? const Size(0, 0),
- lines,
- Offset(nodeLeft+rightNode.nodeSize.width, y),
- Offset(nodeLeft+rightNode.nodeSize.width, lineEndTop));
- }
- }
- }
- void leftNodePosition(List<NodePaintElement> leftList, Size allListSize, List<NodeConnectLine> lines, Offset parentLeftCenter, Offset parentLineStart) {
- final firstNode = leftList[0];
- final firstNodeAndChildrenRegionHeight = firstNode.nodeSize.height > (firstNode.childrenSize?.height ?? 0) ?
- firstNode.nodeSize.height : (firstNode.childrenSize?.height ?? 0) ;
- final lastNode = leftList[leftList.length - 1];
- final lastNodeAndChildrenRegionHeight = lastNode.nodeSize.height > (lastNode.childrenSize?.height ?? 0) ?
- lastNode.nodeSize.height : (lastNode.childrenSize?.height ?? 0);
- // 第一个子节点的中心点Y轴
- final centerGap = (allListSize.height - (firstNodeAndChildrenRegionHeight/2 + lastNodeAndChildrenRegionHeight/2))/2;
- final firstNodeCenterY = parentLeftCenter.dy - (centerGap>0 ? centerGap : 0);
- final nodeRight = parentLeftCenter.dx - theme.nodeVerticalSpace;
- var nodeTop = 0.0; // 每个节点的top
- var nodeLeft = 0.0;
- var y = 0.0; // node 中心y
- for (var i=0;i<leftList.length;i++) {
- final leftNode = leftList[i];
- if (i == 0) {
- y = firstNodeCenterY;
- }else {
- final preNodeHeight = (leftList[i-1].childrenSize?.height ?? 0) > leftList[i-1].nodeSize.height ? (leftList[i-1].childrenSize?.height ?? 0) : leftList[i-1].nodeSize.height;
- final nodeHeight = (leftNode.childrenSize?.height ?? 0) > leftNode.nodeSize.height ? (leftNode.childrenSize?.height ?? 0) : leftNode.nodeSize.height;
- y = y + preNodeHeight /2 + nodeHeight/2 + theme.nodeHorizontalSpace;
- }
- nodeTop = y - leftNode.nodeSize.height/2;
- nodeLeft = nodeRight - leftNode.nodeSize.width;
- leftNode.offset = Offset(nodeLeft, nodeTop);
- nodeElementsPosition(nodeTop, nodeLeft, leftNode);
- final lineEndTop = leftNode.level>1 ? y + leftNode.nodeSize.height/2 + theme.lineWidth : y;
- // 第三级开始有底边
- if (leftNode.level>1) {
- lines.add(NodeConnectLine(Offset(nodeLeft, lineEndTop), Offset(nodeRight,
- lineEndTop)
- ));
- }
- lines.add(NodeConnectLine(parentLineStart,
- Offset(nodeRight, lineEndTop)
- ));
- if (leftNode.children!=null && leftNode.children?.isNotEmpty == true) {
- leftNodePosition(
- leftNode.children!,
- leftNode.childrenSize ?? const Size(0, 0),
- lines,
- Offset(nodeLeft, y),
- Offset(nodeLeft, lineEndTop));
- }
- }
- }
- ///
- /// 计算节点内部的元素位置
- ///
- void nodeElementsPosition(double top, double left, NodePaintElement node) {
- Map<NodeElement, PaintElement> elements = node.paintElements;
- if(elements.isNotEmpty) {
- // 外边框
- if(elements.containsKey(NodeElement.border)) {
- var border = elements[NodeElement.border];
- if (border != null) {
- if(border is RRectPaintElement) {
- border.rrect = RRect.fromLTRBR(left, top, left + border.rrect.width, top + border.rrect.height, border.rrect.blRadius);
- }
- elements[NodeElement.border] = border;
- }
- }
- // 背景
- if(elements.containsKey(NodeElement.background)) {
- var back = elements[NodeElement.background];
- if (back != null) {
- if(back is RRectPaintElement) {
- var backLeft = left;
- var backTop = top;
- if(elements.containsKey(NodeElement.border)) {
- backLeft += theme.lineWidth;
- backTop += theme.lineWidth;
- }
- back.rrect = RRect.fromLTRBR(backLeft, backTop, backLeft+back.rrect.width, backTop+back.rrect.height, back.rrect.blRadius);
- }
- elements[NodeElement.background] = back;
- }
-
- }
- // 文字
- final text = elements[NodeElement.text] as TextPaintElement;
- final textWidth = text.painter.width;
- final textHeight = text.painter.height;
- var bottomWidth = textWidth;
- var bottomHeight = textHeight;
- if(elements.containsKey(NodeElement.priority)) {
- bottomWidth += theme.priorityIconSize + theme.elementGap;
- bottomHeight = bottomHeight > theme.priorityIconSize ? bottomHeight : theme.priorityIconSize;
- }
- if(elements.containsKey(NodeElement.progress)) {
- bottomWidth += theme.progressIconSize + theme.elementGap;
- bottomHeight = bottomHeight > theme.progressIconSize ? bottomHeight : theme.progressIconSize;
- }
- if(elements.containsKey(NodeElement.hyperlink)) {
- bottomWidth += theme.linkIconSize + theme.elementGap;
- bottomHeight = bottomHeight > theme.linkIconSize ? bottomHeight : theme.linkIconSize;
- }
- double leftPadding = (node.nodeSize.width - bottomWidth) / 2;
- double textLeft = left + leftPadding;
- double rectPadding = theme.nodeRectPadding;
- if(node.level == 0) {
- rectPadding = theme.rootRectPadding;
- }else if(node.level == 1) {
- rectPadding = theme.secondRectPadding;
- }
- // 优先级
- if(elements.containsKey(NodeElement.priority)) {
- var priority = elements[NodeElement.priority] as ImagePaintElement;
- var center = bottomHeight > theme.priorityIconSize ? (bottomHeight - theme.priorityIconSize)/2 : (theme.priorityIconSize-bottomHeight)/2;
- var priorityTop = top + node.nodeSize.height - rectPadding - bottomHeight + center;
- priority.rect = Rect.fromLTWH(left+leftPadding, priorityTop, theme.priorityIconSize, theme.priorityIconSize);
- elements[NodeElement.priority] = priority;
- textLeft += theme.priorityIconSize + theme.elementGap;
- }
- // 文字
- var textTopGap = bottomHeight > textHeight ? (bottomHeight - textHeight)/2 : (textHeight-bottomHeight)/2;
- var textTop = top + node.nodeSize.height - rectPadding - bottomHeight + textTopGap;
- text.offset = Offset(textLeft, textTop);
- elements[NodeElement.text] = text;
- // 进度
- if(elements.containsKey(NodeElement.progress)) {
- var progress = elements[NodeElement.progress] as ImagePaintElement;
- var center = bottomHeight > theme.progressIconSize ? (bottomHeight - theme.progressIconSize)/2 : (theme.progressIconSize-bottomHeight)/2;
- var progressTop = top + node.nodeSize.height - rectPadding - bottomHeight + center;
- progress.rect = Rect.fromLTWH(
- textLeft + textWidth + theme.elementGap,
- progressTop,
- theme.progressIconSize,
- theme.progressIconSize);
- elements[NodeElement.progress] = progress;
- }
- // 超链接
- if(elements.containsKey(NodeElement.hyperlink)) {
- var hyperlink = elements[NodeElement.hyperlink] as ImagePaintElement;
- var center = bottomHeight > theme.linkIconSize ? (bottomHeight - theme.linkIconSize)/2 : (theme.linkIconSize-bottomHeight)/2;
- var hyperlinkTop = top + node.nodeSize.height - rectPadding - bottomHeight + center;
- var hyperlinkLeft = elements.containsKey(NodeElement.progress) ? textLeft + textWidth + theme.progressIconSize + theme.elementGap*2 : textLeft + textWidth + theme.elementGap;
- hyperlink.rect = Rect.fromLTWH(
- hyperlinkLeft,
- hyperlinkTop,
- theme.linkIconSize,
- theme.linkIconSize);
- elements[NodeElement.hyperlink] = hyperlink;
- }
- // 图片
- if(elements.containsKey(NodeElement.image)) {
- var image = elements[NodeElement.image] as ImagePaintElement;
- image.rect = Rect.fromLTWH(left+(node.nodeSize.width - image.rect.width) / 2, top+rectPadding, image.rect.width, image.rect.height);
- elements[NodeElement.image] = image;
- }
- node.paintElements = elements;
- }
- }
- }
|