Skip to content

Commit

Permalink
Feature/905/street map layout (#1585)
Browse files Browse the repository at this point in the history
* initial street layout introduction

* may be a  fix for e2e filechooser test

* street layout flattening fix, filechooser e2e fix, global setting dialog layout improvement, code refactoring

* fixed TMStreet layout

* added max treemap files

* fixing linting, added remark about e2e falky test fix, added streetlayout todo

* edited changelog

* fixes after @BridgeAR review

* updated changelog

* Apply code formating #1543

* Update visualization/app/codeCharta/ui/maxTreeMapFiles/maxTreeMapFiles.component.ts

* Update visualization/app/codeCharta/util/algorithm/streetLayout/horizontalStreet.ts

* Update visualization/app/codeCharta/streetLayoutTodo.md

* Update visualization/app/codeCharta/util/algorithm/streetLayout/streetViewHelper.ts

* Update visualization/app/codeCharta/util/algorithm/streetLayout/verticalStreet.ts

* Update visualization/app/codeCharta/util/algorithm/treeMapLayout/treeMapHelper.ts

* Improve identation #905

* Merge main, Fix label position conflicts #905

* Fix scaling bug and improve performance

* Fix label positioning bug on mouse move, Apply minor refactorings.

* Add regression tests for label scaling/positioning bugs

* Fix temporary label bug: reset on map move/turn

Co-authored-by: Cedrik Bormann <cedrikbormann@gmail.com>
Co-authored-by: Cedrik Bormann <26900540+ce-bo@users.noreply.github.com>
  • Loading branch information
3 people committed Dec 23, 2020
1 parent 24d1ad6 commit ff46492
Show file tree
Hide file tree
Showing 70 changed files with 2,078 additions and 109 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/)
- You can click the displayed "download and purge" button to download/backup at least 6 months old Configs and then purge them from the local storage to make space for new ones.
- If we cannot purge any Configs, you might have to do that by your own by deleting specific Configs manually.

- Integrated streetlayout ([#904](https://github.com/MaibornWolff/codecharta/issues/904))
![cc_street_ccv](https://user-images.githubusercontent.com/63230711/78872405-87eed900-7a49-11ea-984a-c0ef738779b9.png)
In street layout file nodes are displayed as buildings and directories are displayed as streets. A street layout has the advantage of a more apparent directory structure and stable positioning of nodes after metric changes.
two different Street layout are integrated : - StreetLayout : as described above. - TMStreet : a combination of street layout and squarified layout.

### Changed

- Increase possible margin size ([#1490](https://github.com/MaibornWolff/codecharta/pull/1490))
Expand All @@ -60,6 +65,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/)

### Chore 👨‍💻 👩‍💻

- e2e flaky test ([#1322](https://github.com/MaibornWolff/codecharta/issues/1322))

## [1.63.0] - 2020-11-30

### Added 🚀
Expand Down
Binary file added visualization/app/codeCharta/assets/empty.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 17 additions & 1 deletion visualization/app/codeCharta/codeCharta.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ExportCCFile } from "./codeCharta.api.model"
import { CodeMapBuilding } from "./ui/codeMap/rendering/codeMapBuilding"
import { FileState } from "./model/files/files"
import { CustomConfig } from "./model/customConfig/customConfig.api.model"
import Rectangle from "./util/algorithm/streetLayout/rectangle"

export interface NameDataPair {
fileName: string
Expand All @@ -17,6 +18,12 @@ export enum SearchPanelMode {
minimized = "minimized"
}

export enum LayoutAlgorithm {
SquarifiedTreeMap = "Squarified TreeMap",
StreetMap = "StreetMap",
TreeMapStreet = "TreeMapStreet"
}

export interface CCFile {
map: CodeMapNode
settings: {
Expand All @@ -25,7 +32,7 @@ export interface CCFile {
fileMeta: FileMeta
}

export interface CodeMapNode {
interface squarifiedNode {
name: string
id?: number
type: NodeType
Expand All @@ -44,6 +51,13 @@ export interface CodeMapNode {
fixedPosition?: FixedPosition
}

interface streetNode {
value?: number
rect?: Rectangle
zOffset?: number
}
export interface CodeMapNode extends squarifiedNode, streetNode {}

export interface FixedPosition {
left: number
top: number
Expand Down Expand Up @@ -123,6 +137,8 @@ export interface AppSettings {
panelSelection: PanelSelection
showMetricLabelNameValue: boolean
showMetricLabelNodeName: boolean
layoutAlgorithm: LayoutAlgorithm
maxTreeMapFiles: number
experimentalFeaturesEnabled: boolean
}

Expand Down
5 changes: 4 additions & 1 deletion visualization/app/codeCharta/state/injector.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ import { NodeSearchService } from "./nodeSearch.service"
import { IsPresentationModeService } from "./store/appSettings/isPresentationMode/isPresentationMode.service"
import { MetricDataService } from "./store/metricData/metricData.service"
import { ExperimentalFeaturesEnabledService } from "./store/appSettings/enableExperimentalFeatures/experimentalFeaturesEnabled.service"

import { LayoutAlgorithmService } from "./store/appSettings/layoutAlgorithm/layoutAlgorithm.service"
import { MaxTreeMapFilesService } from "./store/appSettings/maxTreeMapFiles/maxTreeMapFiles.service"
export class InjectorService {
/* @ngInject */
constructor(
Expand Down Expand Up @@ -104,6 +105,8 @@ export class InjectorService {
private blacklistService: BlacklistService,
private nodeSearchService: NodeSearchService,
private isPresentationModeService: IsPresentationModeService,
private layoutAlgorithmService: LayoutAlgorithmService,
private maxTreeMapFilesService: MaxTreeMapFilesService,
private experimentalFeaturesEnabledService: ExperimentalFeaturesEnabledService
) {}
}
4 changes: 4 additions & 0 deletions visualization/app/codeCharta/state/state.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ import { IsPresentationModeService } from "./store/appSettings/isPresentationMod
import { EdgeMetricDataService } from "./store/metricData/edgeMetricData/edgeMetricData.service"
import { MetricDataService } from "./store/metricData/metricData.service"
import { ExperimentalFeaturesEnabledService } from "./store/appSettings/enableExperimentalFeatures/experimentalFeaturesEnabled.service"
import { LayoutAlgorithmService } from "./store/appSettings/layoutAlgorithm/layoutAlgorithm.service"
import { MaxTreeMapFilesService } from "./store/appSettings/maxTreeMapFiles/maxTreeMapFiles.service"

angular
.module("app.codeCharta.state", ["app.codeCharta"])
Expand Down Expand Up @@ -107,4 +109,6 @@ angular
.service(camelCase(InjectorService.name), InjectorService)
.service(camelCase(StoreService.name), StoreService)
.service(camelCase(NodeSearchService.name), NodeSearchService)
.service(camelCase(LayoutAlgorithmService.name), LayoutAlgorithmService)
.service(camelCase(MaxTreeMapFilesService.name), MaxTreeMapFilesService)
.service(camelCase(ExperimentalFeaturesEnabledService.name), ExperimentalFeaturesEnabledService)
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import { defaultSortingOrderAscending } from "./sortingOrderAscending/sortingOrd
import { defaultSearchPanelMode } from "./searchPanelMode/searchPanelMode.actions"
import { defaultCameraTarget } from "./cameraTarget/cameraTarget.actions"
import { defaultExperimentalFeaturesEnabled } from "./enableExperimentalFeatures/experimentalFeaturesEnabled.actions"
import { defaultLayoutAlgorithm } from "./layoutAlgorithm/layoutAlgorithm.actions"
import { defaultMaxTreeMapFiles } from "./maxTreeMapFiles/maxTreeMapFiles.actions"

export enum AppSettingsActions {
SET_APP_SETTINGS = "SET_APP_SETTINGS"
Expand Down Expand Up @@ -73,5 +75,7 @@ export const defaultAppSettings: AppSettings = {
isLoadingFile: defaultIsLoadingFile,
searchPanelMode: defaultSearchPanelMode,
sortingOrderAscending: defaultSortingOrderAscending,
experimentalFeaturesEnabled: defaultExperimentalFeaturesEnabled
experimentalFeaturesEnabled: defaultExperimentalFeaturesEnabled,
layoutAlgorithm: defaultLayoutAlgorithm,
maxTreeMapFiles: defaultMaxTreeMapFiles
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import { amountOfTopLabels } from "./amountOfTopLabels/amountOfTopLabels.reducer
import { isPresentationMode } from "./isPresentationMode/isPresentationMode.reducer"
import { combineReducers } from "redux"
import { experimentalFeaturesEnabled } from "./enableExperimentalFeatures/experimentalFeaturesEnabled.reducer"
import { layoutAlgorithm } from "./layoutAlgorithm/layoutAlgorithm.reducer"
import { maxTreeMapFiles } from "./maxTreeMapFiles/maxTreeMapFiles.reducer"

const appSettings = combineReducers({
// Plop: Append sub-reducer here
Expand Down Expand Up @@ -54,7 +56,9 @@ const appSettings = combineReducers({
amountOfEdgePreviews,
amountOfTopLabels,
isPresentationMode,
experimentalFeaturesEnabled
experimentalFeaturesEnabled,
layoutAlgorithm,
maxTreeMapFiles
})

export default appSettings
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import { splitAmountOfEdgePreviewsAction } from "./amountOfEdgePreviews/amountOf
import { splitAmountOfTopLabelsAction } from "./amountOfTopLabels/amountOfTopLabels.splitter"
import { splitIsPresentationModeAction } from "./isPresentationMode/isPresentationMode.splitter"
import { splitExperimentalFeaturesEnabledAction } from "./enableExperimentalFeatures/experimentalFeaturesEnabled.splitter"
import { splitLayoutAlgorithmAction } from "./layoutAlgorithm/layoutAlgorithm.splitter"
import { splitMaxTreeMapFilesAction } from "./maxTreeMapFiles/maxTreeMapFiles.splitter"

export function splitAppSettingsActions(payload: RecursivePartial<AppSettings>) {
const actions: CCAction[] = []
Expand Down Expand Up @@ -137,5 +139,13 @@ export function splitAppSettingsActions(payload: RecursivePartial<AppSettings>)
actions.push(splitExperimentalFeaturesEnabledAction(payload.experimentalFeaturesEnabled))
}

if (payload.layoutAlgorithm !== undefined) {
actions.push(splitLayoutAlgorithmAction(payload.layoutAlgorithm))
}

if (payload.maxTreeMapFiles !== undefined) {
actions.push(splitMaxTreeMapFilesAction(payload.maxTreeMapFiles))
}

return actions
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { CCAction, LayoutAlgorithm } from "../../../../codeCharta.model"

export enum LayoutAlgorithmActions {
SET_LAYOUT_ALGORITHM = "SET_LAYOUT_ALGORITHM"
}

export interface SetLayoutAlgorithmAction extends CCAction {
type: LayoutAlgorithmActions.SET_LAYOUT_ALGORITHM
payload: LayoutAlgorithm
}

export type LayoutAlgorithmAction = SetLayoutAlgorithmAction

export function setLayoutAlgorithm(layoutAlgorithm: LayoutAlgorithm = defaultLayoutAlgorithm): SetLayoutAlgorithmAction {
return {
type: LayoutAlgorithmActions.SET_LAYOUT_ALGORITHM,
payload: layoutAlgorithm
}
}

export const defaultLayoutAlgorithm: LayoutAlgorithm = LayoutAlgorithm.SquarifiedTreeMap
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { layoutAlgorithm } from "./layoutAlgorithm.reducer"
import { LayoutAlgorithmAction, setLayoutAlgorithm } from "./layoutAlgorithm.actions"
import { LayoutAlgorithm } from "../../../../codeCharta.model"

describe("layoutAlgorithm", () => {
describe("Default State", () => {
it("should initialize the default state", () => {
const result = layoutAlgorithm(undefined, {} as LayoutAlgorithmAction)
expect(result).toEqual(LayoutAlgorithm.SquarifiedTreeMap)
})
})

describe("setLayoutAlgorithm", () => {
it("should set new layoutAlgorithm", () => {
const result = layoutAlgorithm(LayoutAlgorithm.SquarifiedTreeMap, setLayoutAlgorithm(LayoutAlgorithm.StreetMap))
expect(result).toEqual(LayoutAlgorithm.StreetMap)
})

it("should set default layoutAlgorithm", () => {
const result = layoutAlgorithm(LayoutAlgorithm.StreetMap, setLayoutAlgorithm())
expect(result).toEqual(LayoutAlgorithm.SquarifiedTreeMap)
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { LayoutAlgorithmAction, LayoutAlgorithmActions, setLayoutAlgorithm } from "./layoutAlgorithm.actions"
import { LayoutAlgorithm } from "../../../../codeCharta.model"

export function layoutAlgorithm(state: LayoutAlgorithm = setLayoutAlgorithm().payload, action: LayoutAlgorithmAction): LayoutAlgorithm {
switch (action.type) {
case LayoutAlgorithmActions.SET_LAYOUT_ALGORITHM:
return action.payload
default:
return state
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import "../../../state.module"
import { IRootScopeService } from "angular"
import { StoreService } from "../../../store.service"
import { getService, instantiateModule } from "../../../../../../mocks/ng.mockhelper"
import { LayoutAlgorithmAction, LayoutAlgorithmActions } from "./layoutAlgorithm.actions"
import { LayoutAlgorithmService } from "./layoutAlgorithm.service"
import { withMockedEventMethods } from "../../../../util/dataMocks"
import { LayoutAlgorithm } from "../../../../codeCharta.model"

describe("LayoutAlgorithmService", () => {
let layoutAlgorithmService: LayoutAlgorithmService
let storeService: StoreService
let $rootScope: IRootScopeService

beforeEach(() => {
restartSystem()
rebuildService()
withMockedEventMethods($rootScope)
})

function restartSystem() {
instantiateModule("app.codeCharta.state")

$rootScope = getService<IRootScopeService>("$rootScope")
storeService = getService<StoreService>("storeService")
}

function rebuildService() {
layoutAlgorithmService = new LayoutAlgorithmService($rootScope, storeService)
}

describe("constructor", () => {
it("should subscribe to store", () => {
StoreService.subscribe = jest.fn()

rebuildService()

expect(StoreService.subscribe).toHaveBeenCalledWith($rootScope, layoutAlgorithmService)
})
})

describe("onStoreChanged", () => {
it("should notify all subscribers with the new layoutAlgorithm value", () => {
const action: LayoutAlgorithmAction = {
type: LayoutAlgorithmActions.SET_LAYOUT_ALGORITHM,
payload: LayoutAlgorithm.StreetMap
}
storeService["store"].dispatch(action)

layoutAlgorithmService.onStoreChanged(LayoutAlgorithmActions.SET_LAYOUT_ALGORITHM)

expect($rootScope.$broadcast).toHaveBeenCalledWith("layout-algorithm-changed", { layoutAlgorithm: LayoutAlgorithm.StreetMap })
})

it("should not notify anything on non-layout-algorithm-events", () => {
layoutAlgorithmService.onStoreChanged("ANOTHER_ACTION")

expect($rootScope.$broadcast).not.toHaveBeenCalled()
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { StoreService, StoreSubscriber } from "../../../store.service"
import { IRootScopeService } from "angular"
import { LayoutAlgorithmActions } from "./layoutAlgorithm.actions"
import { LayoutAlgorithm } from "../../../../codeCharta.model"
import { isActionOfType } from "../../../../util/reduxHelper"

export interface LayoutAlgorithmSubscriber {
onLayoutAlgorithmChanged(layoutAlgorithm: LayoutAlgorithm)
}

export class LayoutAlgorithmService implements StoreSubscriber {
private static LAYOUT_ALGORITHM_CHANGED_EVENT = "layout-algorithm-changed"

constructor(private $rootScope: IRootScopeService, private storeService: StoreService) {
StoreService.subscribe(this.$rootScope, this)
}

onStoreChanged(actionType: string) {
if (isActionOfType(actionType, LayoutAlgorithmActions)) {
this.notify(this.select())
}
}

private select() {
return this.storeService.getState().appSettings.layoutAlgorithm
}

private notify(newState: LayoutAlgorithm) {
this.$rootScope.$broadcast(LayoutAlgorithmService.LAYOUT_ALGORITHM_CHANGED_EVENT, { layoutAlgorithm: newState })
}

static subscribe($rootScope: IRootScopeService, subscriber: LayoutAlgorithmSubscriber) {
$rootScope.$on(LayoutAlgorithmService.LAYOUT_ALGORITHM_CHANGED_EVENT, (_event, data) => {
subscriber.onLayoutAlgorithmChanged(data.layoutAlgorithm)
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { LayoutAlgorithmAction, setLayoutAlgorithm } from "./layoutAlgorithm.actions"
import { LayoutAlgorithm } from "../../../../codeCharta.model"

export function splitLayoutAlgorithmAction(payload: LayoutAlgorithm): LayoutAlgorithmAction {
return setLayoutAlgorithm(payload)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { CCAction } from "../../../../codeCharta.model"

export enum MaxTreeMapFilesActions {
SET_MAX_TREE_MAP_FILES = "SET_MAX_TREE_MAP_FILES"
}

export interface SetMaxTreeMapFilesAction extends CCAction {
type: MaxTreeMapFilesActions.SET_MAX_TREE_MAP_FILES
payload: number
}

export type MaxTreeMapFilesAction = SetMaxTreeMapFilesAction

export function setMaxTreeMapFiles(maxTreeMapFiles: number = defaultMaxTreeMapFiles): SetMaxTreeMapFilesAction {
return {
type: MaxTreeMapFilesActions.SET_MAX_TREE_MAP_FILES,
payload: maxTreeMapFiles
}
}

export const defaultMaxTreeMapFiles = 100
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { maxTreeMapFiles } from "./maxTreeMapFiles.reducer"
import { MaxTreeMapFilesAction, setMaxTreeMapFiles } from "./maxTreeMapFiles.actions"

describe("maxTreeMapFiles", () => {
describe("Default State", () => {
it("should initialize the default state", () => {
const result = maxTreeMapFiles(undefined, {} as MaxTreeMapFilesAction)

expect(result).toEqual(100)
})
})

describe("Action: SET_MAX_TREE_MAP_FILES", () => {
it("should set new maxTreeMapFiles", () => {
const result = maxTreeMapFiles(100, setMaxTreeMapFiles(200))

expect(result).toEqual(200)
})

it("should set default maxTreeMapFiles", () => {
const result = maxTreeMapFiles(200, setMaxTreeMapFiles())

expect(result).toEqual(100)
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { MaxTreeMapFilesAction, MaxTreeMapFilesActions, setMaxTreeMapFiles } from "./maxTreeMapFiles.actions"

export function maxTreeMapFiles(state: number = setMaxTreeMapFiles().payload, action: MaxTreeMapFilesAction): number {
switch (action.type) {
case MaxTreeMapFilesActions.SET_MAX_TREE_MAP_FILES:
return action.payload
default:
return state
}
}

0 comments on commit ff46492

Please sign in to comment.