我很早以前就已经使用python完成了一项功能。
我以无法用榆树复制的特定方式绘制甘特图。
这是我的代码:https://ellie-app.com/8sYLsxTZHk5a1
问题出在“ calcTaskPosition”函数中,我尝试在其中设置任务的行。
calcTaskPosition : Int -> Task -> List(Task)
calcTaskPosition row task =
let
precs = List.concatMap (calcTaskPosition (row+1)) (taskPrecs task)
in
{ task | col = (Maybe.withDefault -1 <|
List.maximum <|
List.map (\t -> t.col) precs) + 1
--, row = row
}
:: precs
在我的示例中,任务行由initTask函数设置。我希望得到相同的任务顺序,而不必在initTask函数中设置明确的行位置。
[第一个线索是,当您查看svg时,您会注意到“ task1”实际上已渲染两次。如果您在所发布的代码段中取消注释行--, row = row
,则更易于查看。
使用Elm(和其他功能语言)时,您的任务不会就地进行操作,但是当您对它们进行突变时,它们会被复制。因此(现在)将col和row值保留在模型中并不是真正有用的。另外,使用任务ID比直接链接对象更有意义。
考虑到这一点,我将创建两个不同的任务记录:一个用于将其保存在模型中(在我的示例中为Task
),另一个用于呈现它(我称为DrawableTask
)。然后您需要一个像
toDrawableListOfTasks : List Task -> List DrawableTask
将在视图中调用。
转换功能本质上是使用tasksNotInTaskPrecs
,在此处选择可以立即绘制的所有任务(因为其任务列表为空)。我对其进行了概括,并将其称为allDependenciesMet
,并在每次迭代中都使用它来选择可以绘制的任务。
所有可以绘制的任务将被添加到一个临时列表(在我的情况下是一个字典,用于快速查找已输入的任务),然后下一次迭代将从所有尚未绘制的任务开始。如果没有任务,则可以返回列表,渲染过程将再次遍历该列表。
order : Temp -> List Task -> List DrawableTask
order temp todo =
case List.partition (allDependenciesMet temp) todo of
( [], [] ) ->
-- We are done and can return the list
Dict.values temp
|> List.sortBy .row
( [], _ ) ->
Debug.todo "An invalid list of tasks was passed"
( drawableTasks, nextTodo ) ->
let
nextTemp =
List.indexedMap (toDrawable temp) drawableTasks
|> List.map (\t -> ( t.id, t ))
|> Dict.fromList
|> Dict.union temp
in
order nextTemp nextTodo
我不确定这是否可以理解,但是您应该可以遵循https://ellie-app.com/8tdzrgLfBfya1